blob: e81d7f1b968083e9be5226e7a6fd1ee8d75e6134 [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 McCarneye0c6a2d2020-05-01 11:37:08 -050022#include "rule.hpp"
Matthew Barthbbc7c582020-02-03 15:55:15 -060023#include "utility.hpp"
24
Shawn McCarney415094c2021-02-15 11:08:19 -060025#include <xyz/openbmc_project/Common/error.hpp>
26
Shawn McCarney589c1812021-01-14 12:13:26 -060027#include <algorithm>
Matthew Barthf2bcf1f2020-01-29 14:42:47 -060028#include <chrono>
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050029#include <exception>
Shawn McCarney589c1812021-01-14 12:13:26 -060030#include <functional>
31#include <map>
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050032#include <tuple>
33#include <utility>
Matthew Barth250d0a92020-02-28 13:04:24 -060034#include <variant>
Matthew Barthf2bcf1f2020-01-29 14:42:47 -060035
Shawn McCarney84807b92020-04-30 18:40:03 -050036namespace phosphor::power::regulators
Matthew Barth29e9e382020-01-23 13:40:49 -060037{
38
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050039namespace fs = std::filesystem;
40
Shawn McCarney589c1812021-01-14 12:13:26 -060041constexpr auto busName = "xyz.openbmc_project.Power.Regulators";
42constexpr auto managerObjPath = "/xyz/openbmc_project/power/regulators/manager";
43constexpr auto compatibleIntf =
44 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
45constexpr auto compatibleNamesProp = "Names";
46
47/**
48 * Default configuration file name. This is used when the system does not
49 * implement the D-Bus compatible interface.
50 */
51constexpr auto defaultConfigFileName = "config.json";
52
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -050053/**
54 * Standard configuration file directory. This directory is part of the
55 * firmware install image. It contains the standard version of the config file.
56 */
57const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"};
58
59/**
60 * Test configuration file directory. This directory can contain a test version
61 * of the config file. The test version will override the standard version.
62 */
63const fs::path testConfigFileDir{"/etc/phosphor-regulators"};
64
Matthew Barthf2bcf1f2020-01-29 14:42:47 -060065Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) :
Shawn McCarney589c1812021-01-14 12:13:26 -060066 ManagerObject{bus, managerObjPath, true}, bus{bus}, eventLoop{event},
67 services{bus}
Matthew Barth29e9e382020-01-23 13:40:49 -060068{
Shawn McCarney589c1812021-01-14 12:13:26 -060069 // Subscribe to D-Bus interfacesAdded signal from Entity Manager. This
70 // notifies us if the compatible interface becomes available later.
71 std::string matchStr = sdbusplus::bus::match::rules::interfacesAdded() +
72 sdbusplus::bus::match::rules::sender(
73 "xyz.openbmc_project.EntityManager");
74 std::unique_ptr<sdbusplus::server::match::match> matchPtr =
75 std::make_unique<sdbusplus::server::match::match>(
76 bus, matchStr,
77 std::bind(&Manager::interfacesAddedHandler, this,
78 std::placeholders::_1));
79 signals.emplace_back(std::move(matchPtr));
Matthew Barth250d0a92020-02-28 13:04:24 -060080
Shawn McCarney589c1812021-01-14 12:13:26 -060081 // Try to find compatible system types using D-Bus compatible interface.
82 // Note that it might not be supported on this system, or the service that
83 // provides the interface might not be running yet.
84 findCompatibleSystemTypes();
Shawn McCarney84807b92020-04-30 18:40:03 -050085
Shawn McCarney589c1812021-01-14 12:13:26 -060086 // Try to find and load the JSON configuration file
87 loadConfigFile();
Matthew Barth29e9e382020-01-23 13:40:49 -060088
89 // Obtain dbus service name
90 bus.request_name(busName);
91}
92
93void Manager::configure()
94{
Shawn McCarney9bd94d32021-01-25 19:40:42 -060095 // Clear any cached data or error history related to hardware devices
96 clearHardwareData();
97
Shawn McCarney6345c6c2020-05-04 10:35:05 -050098 // Verify System object exists; this means config file has been loaded
99 if (system)
100 {
101 // Configure the regulator devices in the system
Bob King23243f82020-07-29 10:38:57 +0800102 system->configure(services);
Shawn McCarney6345c6c2020-05-04 10:35:05 -0500103 }
104 else
105 {
Shawn McCarney415094c2021-02-15 11:08:19 -0600106 // Write error message to journal
Shawn McCarneyb464c8b2020-07-16 17:51:38 -0500107 services.getJournal().logError("Unable to configure regulator devices: "
108 "Configuration file not loaded");
Shawn McCarney6345c6c2020-05-04 10:35:05 -0500109
Shawn McCarney0635df22021-03-02 11:58:00 -0600110 // TODO: Add code to wait for EntityManager to publish the compatible
111 // interface before logging this error.
112
Shawn McCarney415094c2021-02-15 11:08:19 -0600113 // Log critical error since regulators could not be configured. Could
114 // cause hardware damage if default regulator settings are very wrong.
Shawn McCarney0635df22021-03-02 11:58:00 -0600115 /*
Shawn McCarney415094c2021-02-15 11:08:19 -0600116 services.getErrorLogging().logConfigFileError(Entry::Level::Critical,
117 services.getJournal());
Shawn McCarney0635df22021-03-02 11:58:00 -0600118 */
Shawn McCarney415094c2021-02-15 11:08:19 -0600119
120 // Throw InternalFailure to propogate error status to D-Bus client
Shawn McCarney0635df22021-03-02 11:58:00 -0600121 /*
Shawn McCarney415094c2021-02-15 11:08:19 -0600122 throw sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure{};
Shawn McCarney0635df22021-03-02 11:58:00 -0600123 */
Shawn McCarney415094c2021-02-15 11:08:19 -0600124 }
Matthew Barth29e9e382020-01-23 13:40:49 -0600125}
126
Shawn McCarney589c1812021-01-14 12:13:26 -0600127void Manager::interfacesAddedHandler(sdbusplus::message::message& msg)
128{
129 // Verify message is valid
130 if (!msg)
131 {
132 return;
133 }
134
135 try
136 {
137 // Read object path for object that was created or had interface added
138 sdbusplus::message::object_path objPath;
139 msg.read(objPath);
140
141 // Read the dictionary whose keys are interface names and whose values
142 // are dictionaries containing the interface property names and values
143 std::map<std::string,
144 std::map<std::string, std::variant<std::vector<std::string>>>>
145 intfProp;
146 msg.read(intfProp);
147
148 // Find the compatible interface, if present
149 auto itIntf = intfProp.find(compatibleIntf);
150 if (itIntf != intfProp.cend())
151 {
152 // Find the Names property of the compatible interface, if present
153 auto itProp = itIntf->second.find(compatibleNamesProp);
154 if (itProp != itIntf->second.cend())
155 {
156 // Get value of Names property
157 auto propValue = std::get<0>(itProp->second);
158 if (!propValue.empty())
159 {
160 // Store list of compatible system types
161 compatibleSystemTypes = propValue;
162
163 // Find and load JSON config file based on system types
164 loadConfigFile();
165 }
166 }
167 }
168 }
169 catch (const std::exception&)
170 {
171 // Error trying to read interfacesAdded message. One possible cause
172 // could be a property whose value is not a std::vector<std::string>.
173 }
174}
175
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500176void Manager::monitor(bool enable)
Matthew Barth29e9e382020-01-23 13:40:49 -0600177{
178 if (enable)
179 {
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500180 /* Temporarily comment out until monitoring is supported.
181 Timer timer(eventLoop, std::bind(&Manager::timerExpired, this));
182 // Set timer as a repeating 1sec timer
183 timer.restart(std::chrono::milliseconds(1000));
184 timers.emplace_back(std::move(timer));
185 */
Matthew Barth29e9e382020-01-23 13:40:49 -0600186 }
187 else
188 {
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500189 /* Temporarily comment out until monitoring is supported.
190 // Delete all timers to disable monitoring
191 timers.clear();
192 */
193
194 // Verify System object exists; this means config file has been loaded
195 if (system)
196 {
197 // Close the regulator devices in the system. Monitoring is
198 // normally disabled because the system is being powered off. The
199 // devices should be closed in case hardware is removed or replaced
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600200 // while the system is powered off.
Bob Kingd692d6d2020-09-14 13:42:57 +0800201 system->closeDevices(services);
Shawn McCarney5b19ea52020-06-02 18:52:56 -0500202 }
Matthew Barth29e9e382020-01-23 13:40:49 -0600203 }
204}
205
Shawn McCarney589c1812021-01-14 12:13:26 -0600206void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
207 const struct signalfd_siginfo* /*sigInfo*/)
208{
209 // Reload the JSON configuration file
210 loadConfigFile();
211}
212
Matthew Barthf2bcf1f2020-01-29 14:42:47 -0600213void Manager::timerExpired()
214{
215 // TODO Analyze, refresh sensor status, and
216 // collect/update telemetry for each regulator
217}
218
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600219void Manager::clearHardwareData()
220{
Shawn McCarney4e0402c2021-02-05 00:08:33 -0600221 // Clear any cached hardware presence data and VPD values
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600222 services.getPresenceService().clearCache();
Shawn McCarney4e0402c2021-02-05 00:08:33 -0600223 services.getVPD().clearCache();
Shawn McCarney9bd94d32021-01-25 19:40:42 -0600224
225 // Verify System object exists; this means config file has been loaded
226 if (system)
227 {
228 // Clear any cached hardware data in the System object
229 system->clearCache();
230 }
231
232 // TODO: Clear error history related to hardware devices
233}
234
Shawn McCarney589c1812021-01-14 12:13:26 -0600235void Manager::findCompatibleSystemTypes()
Matthew Barth7cbc5532020-01-29 15:13:13 -0600236{
Matthew Barthbbc7c582020-02-03 15:55:15 -0600237 using namespace phosphor::power::util;
238
239 try
240 {
Shawn McCarney589c1812021-01-14 12:13:26 -0600241 // Query object mapper for object paths that implement the compatible
242 // interface. Returns a map of object paths to a map of services names
243 // to their interfaces.
244 DbusSubtree subTree = getSubTree(bus, "/xyz/openbmc_project/inventory",
245 compatibleIntf, 0);
246
247 // Get the first object path
248 auto objectIt = subTree.cbegin();
249 if (objectIt != subTree.cend())
Matthew Barthbbc7c582020-02-03 15:55:15 -0600250 {
Shawn McCarney589c1812021-01-14 12:13:26 -0600251 std::string objPath = objectIt->first;
252
253 // Get the first service name
254 auto serviceIt = objectIt->second.cbegin();
255 if (serviceIt != objectIt->second.cend())
256 {
257 std::string service = serviceIt->first;
258 if (!service.empty())
259 {
260 // Get compatible system types property value
261 getProperty(compatibleIntf, compatibleNamesProp, objPath,
262 service, bus, compatibleSystemTypes);
263 }
264 }
Matthew Barthbbc7c582020-02-03 15:55:15 -0600265 }
266 }
Shawn McCarney589c1812021-01-14 12:13:26 -0600267 catch (const std::exception&)
Matthew Barthbbc7c582020-02-03 15:55:15 -0600268 {
Shawn McCarney589c1812021-01-14 12:13:26 -0600269 // Compatible system types information is not available. The current
270 // system might not support the interface, or the service that
271 // implements the interface might not be running yet.
Matthew Barthbbc7c582020-02-03 15:55:15 -0600272 }
Matthew Barthbbc7c582020-02-03 15:55:15 -0600273}
274
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500275fs::path Manager::findConfigFile()
276{
Shawn McCarney589c1812021-01-14 12:13:26 -0600277 // Build list of possible base file names
278 std::vector<std::string> fileNames{};
279
280 // Add possible file names based on compatible system types (if any)
281 for (const std::string& systemType : compatibleSystemTypes)
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500282 {
Shawn McCarney589c1812021-01-14 12:13:26 -0600283 // Replace all spaces and commas in system type name with underscores
284 std::string fileName{systemType};
285 std::replace(fileName.begin(), fileName.end(), ' ', '_');
286 std::replace(fileName.begin(), fileName.end(), ',', '_');
287
288 // Append .json suffix and add to list
289 fileName.append(".json");
290 fileNames.emplace_back(fileName);
291 }
292
293 // Add default file name for systems that don't use compatible interface
294 fileNames.emplace_back(defaultConfigFileName);
295
296 // Look for a config file with one of the possible base names
297 for (const std::string& fileName : fileNames)
298 {
299 // Check if file exists in test directory
300 fs::path pathName{testConfigFileDir / fileName};
301 if (fs::exists(pathName))
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500302 {
Shawn McCarney589c1812021-01-14 12:13:26 -0600303 return pathName;
304 }
305
306 // Check if file exists in standard directory
307 pathName = standardConfigFileDir / fileName;
308 if (fs::exists(pathName))
309 {
310 return pathName;
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500311 }
312 }
313
Shawn McCarney589c1812021-01-14 12:13:26 -0600314 // No config file found; return empty path
315 return fs::path{};
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500316}
317
318void Manager::loadConfigFile()
319{
320 try
321 {
322 // Find the absolute path to the config file
323 fs::path pathName = findConfigFile();
Shawn McCarney589c1812021-01-14 12:13:26 -0600324 if (!pathName.empty())
325 {
326 // Log info message in journal; config file path is important
327 services.getJournal().logInfo("Loading configuration file " +
328 pathName.string());
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500329
Shawn McCarney589c1812021-01-14 12:13:26 -0600330 // Parse the config file
331 std::vector<std::unique_ptr<Rule>> rules{};
332 std::vector<std::unique_ptr<Chassis>> chassis{};
333 std::tie(rules, chassis) = config_file_parser::parse(pathName);
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500334
Shawn McCarney589c1812021-01-14 12:13:26 -0600335 // Store config file information in a new System object. The old
336 // System object, if any, is automatically deleted.
337 system =
338 std::make_unique<System>(std::move(rules), std::move(chassis));
339 }
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500340 }
341 catch (const std::exception& e)
342 {
343 // Log error messages in journal
Shawn McCarneyb464c8b2020-07-16 17:51:38 -0500344 services.getJournal().logError(exception_utils::getMessages(e));
345 services.getJournal().logError("Unable to load configuration file");
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500346
Shawn McCarney415094c2021-02-15 11:08:19 -0600347 // Log error
348 services.getErrorLogging().logConfigFileError(Entry::Level::Error,
349 services.getJournal());
Shawn McCarneye0c6a2d2020-05-01 11:37:08 -0500350 }
351}
352
Shawn McCarney84807b92020-04-30 18:40:03 -0500353} // namespace phosphor::power::regulators