blob: bd1677671bca331de59ddc4ced72758f34b1d341 [file] [log] [blame]
Matthew Barth29e9e382020-01-23 13:40:49 -06001/**
2 * Copyright © 2020 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 "manager.hpp"
18
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050019#include "chassis.hpp"
20#include "config_file_parser.hpp"
21#include "exception_utils.hpp"
Shawn McCarneycee2e202024-08-02 16:38:33 -050022#include "format_utils.hpp"
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050023#include "rule.hpp"
Matthew Barthbbc7c582020-02-03 15:55:15 -060024#include "utility.hpp"
25
Shawn McCarney415094c2021-02-15 11:08:19 -060026#include <xyz/openbmc_project/Common/error.hpp>
Shawn McCarneyd9c8be52021-05-18 10:07:53 -050027#include <xyz/openbmc_project/State/Chassis/server.hpp>
Shawn McCarney415094c2021-02-15 11:08:19 -060028
Matthew Barthf2bcf1f2020-01-29 14:42:47 -060029#include <chrono>
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050030#include <exception>
Shawn McCarney589c1812021-01-14 12:13:26 -060031#include <functional>
Shawn McCarneycee2e202024-08-02 16:38:33 -050032#include <span>
Shawn McCarney8acaf542021-03-30 10:38:37 -050033#include <thread>
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050034#include <tuple>
35#include <utility>
Matthew Barthf2bcf1f2020-01-29 14:42:47 -060036
Shawn McCarney84807b92020-04-30 18:40:03 -050037namespace phosphor::power::regulators
Matthew Barth29e9e382020-01-23 13:40:49 -060038{
39
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050040namespace fs = std::filesystem;
41
Shawn McCarney589c1812021-01-14 12:13:26 -060042constexpr auto busName = "xyz.openbmc_project.Power.Regulators";
43constexpr auto managerObjPath = "/xyz/openbmc_project/power/regulators/manager";
Shawn McCarneyd9c8be52021-05-18 10:07:53 -050044constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0";
45constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis";
46constexpr auto chassisStateProp = "CurrentPowerState";
Shawn McCarney8acaf542021-03-30 10:38:37 -050047constexpr std::chrono::minutes maxTimeToWaitForCompatTypes{5};
Shawn McCarney589c1812021-01-14 12:13:26 -060048
Shawn McCarneyd9c8be52021-05-18 10:07:53 -050049using PowerState =
50 sdbusplus::xyz::openbmc_project::State::server::Chassis::PowerState;
51
Shawn McCarney589c1812021-01-14 12:13:26 -060052/**
53 * Default configuration file name. This is used when the system does not
54 * implement the D-Bus compatible interface.
55 */
56constexpr auto defaultConfigFileName = "config.json";
57
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050058/**
59 * Standard configuration file directory. This directory is part of the
60 * firmware install image. It contains the standard version of the config file.
61 */
62const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"};
63
64/**
65 * Test configuration file directory. This directory can contain a test version
66 * of the config file. The test version will override the standard version.
67 */
68const fs::path testConfigFileDir{"/etc/phosphor-regulators"};
69
Patrick Williams7354ce62022-07-22 19:26:56 -050070Manager::Manager(sdbusplus::bus_t& bus, const sdeventplus::Event& event) :
Zev Weissd1307292022-04-19 17:13:28 -070071 ManagerObject{bus, managerObjPath}, bus{bus}, eventLoop{event},
Patrick Williams48781ae2023-05-10 07:50:50 -050072 services{bus},
73 phaseFaultTimer{event, std::bind(&Manager::phaseFaultTimerExpired, this)},
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -050074 sensorTimer{event, std::bind(&Manager::sensorTimerExpired, this)}
Matthew Barth29e9e382020-01-23 13:40:49 -060075{
Shawn McCarneycee2e202024-08-02 16:38:33 -050076 // Create object to find compatible system types for current system.
77 // Note that some systems do not provide this information.
78 compatSysTypesFinder = std::make_unique<util::CompatibleSystemTypesFinder>(
79 bus, std::bind_front(&Manager::compatibleSystemTypesFound, this));
Matthew Barth250d0a92020-02-28 13:04:24 -060080
Shawn McCarneycee2e202024-08-02 16:38:33 -050081 // If no system types found so far, try to load default config file
82 if (compatibleSystemTypes.empty())
83 {
84 loadConfigFile();
85 }
Matthew Barth29e9e382020-01-23 13:40:49 -060086
Shawn McCarneyd9c8be52021-05-18 10:07:53 -050087 // Obtain D-Bus service name
Matthew Barth29e9e382020-01-23 13:40:49 -060088 bus.request_name(busName);
Shawn McCarneyd9c8be52021-05-18 10:07:53 -050089
90 // If system is already powered on, enable monitoring
91 if (isSystemPoweredOn())
92 {
93 monitor(true);
94 }
Matthew Barth29e9e382020-01-23 13:40:49 -060095}
96
97void Manager::configure()
98{
Shawn McCarney9bd94d32021-01-25 19:40:42 -060099 // Clear any cached data or error history related to hardware devices
100 clearHardwareData();
101
Shawn McCarney8acaf542021-03-30 10:38:37 -0500102 // Wait until the config file has been loaded or hit max wait time
103 waitUntilConfigFileLoaded();
104
105 // Verify config file has been loaded and System object is valid
106 if (isConfigFileLoaded())
Shawn McCarney6345c6c2020-05-04 10:35:05 -0500107 {
108 // Configure the regulator devices in the system
Bob King23243f82020-07-29 10:38:57 +0800109 system->configure(services);
Shawn McCarney6345c6c2020-05-04 10:35:05 -0500110 }
111 else
112 {
Shawn McCarney415094c2021-02-15 11:08:19 -0600113 // Write error message to journal
Shawn McCarneyb464c8b2020-07-16 17:51:38 -0500114 services.getJournal().logError("Unable to configure regulator devices: "
115 "Configuration file not loaded");
Shawn McCarney6345c6c2020-05-04 10:35:05 -0500116
Shawn McCarney415094c2021-02-15 11:08:19 -0600117 // Log critical error since regulators could not be configured. Could
118 // cause hardware damage if default regulator settings are very wrong.
119 services.getErrorLogging().logConfigFileError(Entry::Level::Critical,
120 services.getJournal());
121
122 // Throw InternalFailure to propogate error status to D-Bus client
123 throw sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure{};
124 }
Matthew Barth29e9e382020-01-23 13:40:49 -0600125}
126
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500127void Manager::monitor(bool enable)
Matthew Barth29e9e382020-01-23 13:40:49 -0600128{
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500129 // Check whether already in the requested monitoring state
130 if (enable == isMonitoringEnabled)
Matthew Barth29e9e382020-01-23 13:40:49 -0600131 {
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500132 return;
133 }
134
135 isMonitoringEnabled = enable;
136 if (isMonitoringEnabled)
137 {
138 services.getJournal().logDebug("Monitoring enabled");
139
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -0500140 // Restart phase fault detection timer with repeating 15 second interval
141 phaseFaultTimer.restart(std::chrono::seconds(15));
142
143 // Restart sensor monitoring timer with repeating 1 second interval
144 sensorTimer.restart(std::chrono::seconds(1));
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500145
146 // Enable sensors service; put all sensors in an active state
147 services.getSensors().enable();
Matthew Barth29e9e382020-01-23 13:40:49 -0600148 }
149 else
150 {
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500151 services.getJournal().logDebug("Monitoring disabled");
152
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -0500153 // Disable timers
154 phaseFaultTimer.setEnabled(false);
155 sensorTimer.setEnabled(false);
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500156
157 // Disable sensors service; put all sensors in an inactive state
158 services.getSensors().disable();
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500159
Shawn McCarney8acaf542021-03-30 10:38:37 -0500160 // Verify config file has been loaded and System object is valid
161 if (isConfigFileLoaded())
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500162 {
163 // Close the regulator devices in the system. Monitoring is
164 // normally disabled because the system is being powered off. The
165 // devices should be closed in case hardware is removed or replaced
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600166 // while the system is powered off.
Bob Kingd692d6d2020-09-14 13:42:57 +0800167 system->closeDevices(services);
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500168 }
Matthew Barth29e9e382020-01-23 13:40:49 -0600169 }
170}
171
Shawn McCarneycee2e202024-08-02 16:38:33 -0500172void Manager::compatibleSystemTypesFound(const std::vector<std::string>& types)
173{
174 // If we don't already have compatible system types
175 if (compatibleSystemTypes.empty())
176 {
177 std::string typesStr = format_utils::toString(std::span{types});
178 services.getJournal().logInfo(
179 std::format("Compatible system types found: {}", typesStr));
180
181 // Store compatible system types
182 compatibleSystemTypes = types;
183
184 // Find and load JSON config file based on system types
185 loadConfigFile();
186 }
187}
188
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -0500189void Manager::phaseFaultTimerExpired()
Shawn McCarney589c1812021-01-14 12:13:26 -0600190{
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -0500191 // Verify config file has been loaded and System object is valid
192 if (isConfigFileLoaded())
193 {
194 // Detect redundant phase faults in regulator devices in the system
195 system->detectPhaseFaults(services);
196 }
Shawn McCarney589c1812021-01-14 12:13:26 -0600197}
198
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -0500199void Manager::sensorTimerExpired()
Matthew Barthf2bcf1f2020-01-29 14:42:47 -0600200{
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500201 // Notify sensors service that a sensor monitoring cycle is starting
202 services.getSensors().startCycle();
203
204 // Verify config file has been loaded and System object is valid
205 if (isConfigFileLoaded())
206 {
207 // Monitor sensors for the voltage rails in the system
208 system->monitorSensors(services);
209 }
210
211 // Notify sensors service that current sensor monitoring cycle has ended
212 services.getSensors().endCycle();
Matthew Barthf2bcf1f2020-01-29 14:42:47 -0600213}
214
Shawn McCarneyc8cbeac2021-09-10 15:42:56 -0500215void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
216 const struct signalfd_siginfo* /*sigInfo*/)
217{
218 // Reload the JSON configuration file
219 loadConfigFile();
220}
221
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600222void Manager::clearHardwareData()
223{
Shawn McCarney4e0402c2021-02-05 00:08:33 -0600224 // Clear any cached hardware presence data and VPD values
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600225 services.getPresenceService().clearCache();
Shawn McCarney4e0402c2021-02-05 00:08:33 -0600226 services.getVPD().clearCache();
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600227
Shawn McCarney8acaf542021-03-30 10:38:37 -0500228 // Verify config file has been loaded and System object is valid
229 if (isConfigFileLoaded())
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600230 {
231 // Clear any cached hardware data in the System object
232 system->clearCache();
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600233
Shawn McCarneyce540f32021-05-14 17:08:41 -0500234 // Clear error history related to hardware devices in the System object
235 system->clearErrorHistory();
236 }
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600237}
238
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500239fs::path Manager::findConfigFile()
240{
Shawn McCarney589c1812021-01-14 12:13:26 -0600241 // Build list of possible base file names
242 std::vector<std::string> fileNames{};
243
244 // Add possible file names based on compatible system types (if any)
245 for (const std::string& systemType : compatibleSystemTypes)
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500246 {
Shawn McCarneycee2e202024-08-02 16:38:33 -0500247 // Look for file name that is entire system type + ".json"
248 // Example: com.acme.Hardware.Chassis.Model.MegaServer.json
249 fileNames.emplace_back(systemType + ".json");
Shawn McCarney589c1812021-01-14 12:13:26 -0600250
Shawn McCarneycee2e202024-08-02 16:38:33 -0500251 // Look for file name that is last node of system type + ".json"
252 // Example: MegaServer.json
253 std::string::size_type pos = systemType.rfind('.');
254 if ((pos != std::string::npos) && ((systemType.size() - pos) > 1))
255 {
256 fileNames.emplace_back(systemType.substr(pos + 1) + ".json");
257 }
Shawn McCarney589c1812021-01-14 12:13:26 -0600258 }
259
260 // Add default file name for systems that don't use compatible interface
261 fileNames.emplace_back(defaultConfigFileName);
262
263 // Look for a config file with one of the possible base names
264 for (const std::string& fileName : fileNames)
265 {
266 // Check if file exists in test directory
267 fs::path pathName{testConfigFileDir / fileName};
268 if (fs::exists(pathName))
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500269 {
Shawn McCarney589c1812021-01-14 12:13:26 -0600270 return pathName;
271 }
272
273 // Check if file exists in standard directory
274 pathName = standardConfigFileDir / fileName;
275 if (fs::exists(pathName))
276 {
277 return pathName;
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500278 }
279 }
280
Shawn McCarney589c1812021-01-14 12:13:26 -0600281 // No config file found; return empty path
282 return fs::path{};
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500283}
284
Shawn McCarneyd9c8be52021-05-18 10:07:53 -0500285bool Manager::isSystemPoweredOn()
286{
287 bool isOn{false};
288
289 try
290 {
291 // Get D-Bus property that contains the current power state for
292 // chassis0, which represents the entire system (all chassis)
293 using namespace phosphor::power::util;
294 auto service = getService(chassisStatePath, chassisStateIntf, bus);
295 if (!service.empty())
296 {
297 PowerState currentPowerState;
298 getProperty(chassisStateIntf, chassisStateProp, chassisStatePath,
299 service, bus, currentPowerState);
300 if (currentPowerState == PowerState::On)
301 {
302 isOn = true;
303 }
304 }
305 }
306 catch (const std::exception& e)
307 {
308 // Current power state might not be available yet. The regulators
309 // application can start before the power state is published on D-Bus.
310 }
311
312 return isOn;
313}
314
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500315void Manager::loadConfigFile()
316{
317 try
318 {
319 // Find the absolute path to the config file
320 fs::path pathName = findConfigFile();
Shawn McCarney589c1812021-01-14 12:13:26 -0600321 if (!pathName.empty())
322 {
323 // Log info message in journal; config file path is important
Patrick Williamsf5402192024-08-16 15:20:53 -0400324 services.getJournal().logInfo(
325 "Loading configuration file " + pathName.string());
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500326
Shawn McCarney589c1812021-01-14 12:13:26 -0600327 // Parse the config file
328 std::vector<std::unique_ptr<Rule>> rules{};
329 std::vector<std::unique_ptr<Chassis>> chassis{};
330 std::tie(rules, chassis) = config_file_parser::parse(pathName);
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500331
Shawn McCarney589c1812021-01-14 12:13:26 -0600332 // Store config file information in a new System object. The old
333 // System object, if any, is automatically deleted.
Patrick Williamsf5402192024-08-16 15:20:53 -0400334 system =
335 std::make_unique<System>(std::move(rules), std::move(chassis));
Shawn McCarney589c1812021-01-14 12:13:26 -0600336 }
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500337 }
338 catch (const std::exception& e)
339 {
340 // Log error messages in journal
Shawn McCarneyb464c8b2020-07-16 17:51:38 -0500341 services.getJournal().logError(exception_utils::getMessages(e));
342 services.getJournal().logError("Unable to load configuration file");
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500343
Shawn McCarney415094c2021-02-15 11:08:19 -0600344 // Log error
345 services.getErrorLogging().logConfigFileError(Entry::Level::Error,
346 services.getJournal());
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500347 }
348}
349
Shawn McCarney8acaf542021-03-30 10:38:37 -0500350void Manager::waitUntilConfigFileLoaded()
351{
352 // If config file not loaded and list of compatible system types is empty
353 if (!isConfigFileLoaded() && compatibleSystemTypes.empty())
354 {
355 // Loop until compatible system types found or waited max amount of time
356 auto start = std::chrono::system_clock::now();
357 std::chrono::system_clock::duration timeWaited{0};
358 while (compatibleSystemTypes.empty() &&
359 (timeWaited <= maxTimeToWaitForCompatTypes))
360 {
Shawn McCarneycee2e202024-08-02 16:38:33 -0500361 // Try to find list of compatible system types. Force finder object
362 // to re-find system types on D-Bus because we are not receiving
363 // InterfacesAdded signals within this while loop.
364 compatSysTypesFinder->refind();
365 if (compatibleSystemTypes.empty())
Shawn McCarney8acaf542021-03-30 10:38:37 -0500366 {
Shawn McCarneycee2e202024-08-02 16:38:33 -0500367 // Not found; sleep 5 seconds
Shawn McCarney8acaf542021-03-30 10:38:37 -0500368 using namespace std::chrono_literals;
369 std::this_thread::sleep_for(5s);
370 }
371 timeWaited = std::chrono::system_clock::now() - start;
372 }
373 }
374}
375
Shawn McCarney84807b92020-04-30 18:40:03 -0500376} // namespace phosphor::power::regulators