blob: 3989bca6ff037aed24e521022eb96b644437dfda [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
3#include "chassis_state_manager.hpp"
4
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -06005#include "utils.hpp"
Andrew Geisslere426b582020-05-28 12:40:55 -05006#include "xyz/openbmc_project/Common/error.hpp"
7#include "xyz/openbmc_project/State/Shutdown/Power/error.hpp"
8
Potin Lai70f36d82022-03-15 10:25:39 +08009#include <fmt/format.h>
10
Andrew Geisslere426b582020-05-28 12:40:55 -050011#include <cereal/archives/json.hpp>
12#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050013#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060014#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070015#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070016#include <sdeventplus/event.hpp>
17#include <sdeventplus/exception.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000018#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050019
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060020#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050021#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050022
Andrew Geisslera90a31a2016-12-13 16:16:28 -060023namespace phosphor
24{
25namespace state
26{
27namespace manager
28{
29
Andrew Geissler8ffdb262021-09-20 15:25:19 -050030PHOSPHOR_LOG2_USING;
31
Andrew Geisslera90a31a2016-12-13 16:16:28 -060032// When you see server:: you know we're referencing our base class
33namespace server = sdbusplus::xyz::openbmc_project::State::server;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000034namespace decoratorServer =
35 sdbusplus::xyz::openbmc_project::State::Decorator::server;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060036
37using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070038using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050039using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050040using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080041constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
42 "obmc-chassis-poweroff@{}.target";
43constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
44 "obmc-chassis-hard-poweroff@{}.target";
45constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
46constexpr auto RESET_HOST_SENSORS_SVC_FMT =
47 "phosphor-reset-sensor-states@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060048constexpr auto ACTIVE_STATE = "active";
49constexpr auto ACTIVATING_STATE = "activating";
50
Andrew Geissler2cf2a262022-02-02 14:38:41 -060051// Details at https://upower.freedesktop.org/docs/Device.html
52constexpr uint TYPE_UPS = 3;
53constexpr uint STATE_FULLY_CHARGED = 4;
54constexpr uint BATTERY_LVL_FULL = 8;
55
Andrew Geissler58a18012018-01-19 19:36:05 -080056constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
57constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060058constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
59
Josh D. King697474c2017-03-02 11:15:55 -060060constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
61constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
62
Andrew Geissler8b1f8622022-01-28 16:37:07 -060063constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
64constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
65constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
66constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000067constexpr auto POWERSYSINPUTS_INTERFACE =
68 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060069constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
70
Andrew Geissler0029a5d2017-01-24 14:48:35 -060071void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060072{
Andrew Geissler3a30b052019-05-14 15:54:37 -050073 try
74 {
75 auto method = this->bus.new_method_call(
76 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050077 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050078 }
Andrew Geissler8ffdb262021-09-20 15:25:19 -050079 catch (const sdbusplus::exception::exception& e)
Andrew Geissler3a30b052019-05-14 15:54:37 -050080 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050081 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler3a30b052019-05-14 15:54:37 -050082 elog<InternalFailure>();
83 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060084
Andrew Geissler0029a5d2017-01-24 14:48:35 -060085 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060086}
87
Potin Lai70f36d82022-03-15 10:25:39 +080088void Chassis::createSystemdTargetTable()
89{
90 systemdTargetTable = {
91 // Use the hard off target to ensure we shutdown immediately
92 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
93 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)}};
94}
95
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060096// TODO - Will be rewritten once sdbusplus client bindings are in place
97// and persistent storage design is in place and sdbusplus
98// has read property function
99void Chassis::determineInitialState()
100{
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600101
102 // Monitor for any properties changed signals on UPower device path
103 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
104 bus,
105 sdbusplus::bus::match::rules::propertiesChangedNamespace(
106 "/org/freedesktop/UPower", UPOWER_INTERFACE),
107 [this](auto& msg) { this->uPowerChangeEvent(msg); });
108
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000109 // Monitor for any properties changed signals on PowerSystemInputs
110 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
111 bus,
112 sdbusplus::bus::match::rules::propertiesChangedNamespace(
113 fmt::format(
114 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
115 POWERSYSINPUTS_INTERFACE),
116 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
117
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600118 determineStatusOfPower();
119
Patrick Williams2975e262020-05-13 18:01:09 -0500120 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800121 auto method = this->bus.new_method_call(
122 "org.openbmc.control.Power", "/org/openbmc/control/power0",
123 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600124
125 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700126 try
127 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700128 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500129 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700130
Patrick Williams37413dc2020-05-13 11:29:54 -0500131 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700132 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500133 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700134 server::Chassis::currentPowerState(PowerState::On);
135 server::Chassis::requestedPowerTransition(Transition::On);
136 return;
137 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500138 else
139 {
140 // The system is off. If we think it should be on then
141 // we probably lost AC while up, so set a new state
142 // change time.
143 uint64_t lastTime;
144 PowerState lastState;
145
146 if (deserializeStateChangeTime(lastTime, lastState))
147 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600148 // If power was on before the BMC reboot and the reboot reason
149 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500150 if (lastState == PowerState::On)
151 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600152 info(
153 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600154
155 // Reset host sensors since system is off now
Potin Lai70f36d82022-03-15 10:25:39 +0800156 startUnit(fmt::format(RESET_HOST_SENSORS_SVC_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600157
Matt Spinler9eab9862018-07-11 14:13:52 -0500158 setStateChangeTime();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600159
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000160 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600161 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000162 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600163 {
164 if (standbyVoltageRegulatorFault())
165 {
166 report<Regulator>();
167 }
168 else
169 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400170 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600171 }
172 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000173 else
174 {
175 info("Pinhole reset");
176 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500177 }
178 }
179 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700180 }
Patrick Williams0a675212021-09-02 09:49:43 -0500181 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700182 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700183 // It's acceptable for the pgood state service to not be available
184 // since it will notify us of the pgood state when it comes up.
185 if (e.name() != nullptr &&
186 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
187 {
188 goto fail;
189 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600190
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700191 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500192 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700193 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600194 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700195
196fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500197 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700198 server::Chassis::currentPowerState(PowerState::Off);
199 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600200
201 return;
202}
203
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600204void Chassis::determineStatusOfPower()
205{
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400206 bool powerGood = determineStatusOfUPSPower();
207 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000208 {
209 return;
210 }
211
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400212 powerGood = determineStatusOfPSUPower();
213 if (powerGood)
214 {
215 // All checks passed, set power status to good
216 server::Chassis::currentPowerStatus(PowerStatus::Good);
217 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000218}
219
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400220bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000221{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600222 // Find all implementations of the UPower interface
223 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
224 MAPPER_INTERFACE, "GetSubTree");
225
226 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
227
228 std::map<std::string, std::map<std::string, std::vector<std::string>>>
229 mapperResponse;
230
231 try
232 {
233 auto mapperResponseMsg = bus.call(mapper);
234 mapperResponseMsg.read(mapperResponse);
235 }
236 catch (const sdbusplus::exception::exception& e)
237 {
238 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
239 throw;
240 }
241
242 if (mapperResponse.empty())
243 {
244 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600245 }
246
247 // Iterate through all returned Upower interfaces and look for UPS's
248 for (const auto& [path, services] : mapperResponse)
249 {
250 for (const auto& serviceIter : services)
251 {
252 const std::string& service = serviceIter.first;
253
254 try
255 {
256 auto method = bus.new_method_call(service.c_str(), path.c_str(),
257 PROPERTY_INTERFACE, "GetAll");
258 method.append(UPOWER_INTERFACE);
259
260 auto response = bus.call(method);
261 using Property = std::string;
262 using Value = std::variant<bool, uint>;
263 using PropertyMap = std::map<Property, Value>;
264 PropertyMap properties;
265 response.read(properties);
266
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600267 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
268 {
269 info("UPower device {OBJ_PATH} is not a UPS device",
270 "OBJ_PATH", path);
271 continue;
272 }
273
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600274 if (std::get<bool>(properties["IsPresent"]) != true)
275 {
276 // There is a UPS detected but it is not officially
277 // "present" yet. Monitor it for state change.
278 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
279 path);
280 continue;
281 }
282
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600283 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
284 {
285 info("UPS is fully charged");
286 }
287 else
288 {
289 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
290 std::get<uint>(properties["State"]));
291 server::Chassis::currentPowerStatus(
292 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400293 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600294 }
295
296 if (std::get<uint>(properties["BatteryLevel"]) ==
297 BATTERY_LVL_FULL)
298 {
299 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600300 // Only one UPS per system, we've found it and it's all
301 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400302 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600303 }
304 else
305 {
306 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
307 "UPS_BAT_LEVEL",
308 std::get<uint>(properties["BatteryLevel"]));
309 server::Chassis::currentPowerStatus(
310 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400311 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600312 }
313 }
314 catch (const sdbusplus::exception::exception& e)
315 {
316 error("Error reading UPS property, error: {ERROR}, "
317 "service: {SERVICE} path: {PATH}",
318 "ERROR", e, "SERVICE", service, "PATH", path);
319 throw;
320 }
321 }
322 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400323 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600324}
325
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400326bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000327{
328 // Find all implementations of the PowerSystemInputs interface
329 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
330 MAPPER_INTERFACE, "GetSubTree");
331
332 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
333
334 std::map<std::string, std::map<std::string, std::vector<std::string>>>
335 mapperResponse;
336
337 try
338 {
339 auto mapperResponseMsg = bus.call(mapper);
340 mapperResponseMsg.read(mapperResponse);
341 }
342 catch (const sdbusplus::exception::exception& e)
343 {
344 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
345 "ERROR", e);
346 throw;
347 }
348
349 for (const auto& [path, services] : mapperResponse)
350 {
351 for (const auto& serviceIter : services)
352 {
353 const std::string& service = serviceIter.first;
354
355 try
356 {
357 auto method = bus.new_method_call(service.c_str(), path.c_str(),
358 PROPERTY_INTERFACE, "GetAll");
359 method.append(POWERSYSINPUTS_INTERFACE);
360
361 auto response = bus.call(method);
362 using Property = std::string;
363 using Value = std::variant<std::string>;
364 using PropertyMap = std::map<Property, Value>;
365 PropertyMap properties;
366 response.read(properties);
367
368 auto statusStr = std::get<std::string>(properties["Status"]);
369 auto status =
370 decoratorServer::PowerSystemInputs::convertStatusFromString(
371 statusStr);
372
373 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
374 {
375 info("Power System Inputs status is in Fault state");
376 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400377 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000378 }
379 }
380 catch (const sdbusplus::exception::exception& e)
381 {
382 error(
383 "Error reading Power System Inputs property, error: {ERROR}, "
384 "service: {SERVICE} path: {PATH}",
385 "ERROR", e, "SERVICE", service, "PATH", path);
386 throw;
387 }
388 }
389 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400390 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000391}
392
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600393void Chassis::uPowerChangeEvent(sdbusplus::message::message& msg)
394{
395 debug("UPS Property Change Event Triggered");
396 std::string statusInterface;
397 std::map<std::string, std::variant<uint, bool>> msgData;
398 msg.read(statusInterface, msgData);
399
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000400 // If the change is to any of the properties we are interested in, then call
401 // determineStatusOfPower(), which looks at all the power-related
402 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600403 auto propertyMap = msgData.find("IsPresent");
404 if (propertyMap != msgData.end())
405 {
406 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
407 std::get<bool>(propertyMap->second));
408 determineStatusOfPower();
409 return;
410 }
411
412 propertyMap = msgData.find("State");
413 if (propertyMap != msgData.end())
414 {
415 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
416 std::get<uint>(propertyMap->second));
417 determineStatusOfPower();
418 return;
419 }
420
421 propertyMap = msgData.find("BatteryLevel");
422 if (propertyMap != msgData.end())
423 {
424 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
425 std::get<uint>(propertyMap->second));
426 determineStatusOfPower();
427 return;
428 }
429 return;
430}
431
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000432void Chassis::powerSysInputsChangeEvent(sdbusplus::message::message& msg)
433{
434 debug("Power System Inputs Property Change Event Triggered");
435 std::string statusInterface;
436 std::map<std::string, std::variant<std::string>> msgData;
437 msg.read(statusInterface, msgData);
438
439 // If the change is to any of the properties we are interested in, then call
440 // determineStatusOfPower(), which looks at all the power-related
441 // interfaces, to see if a power status change is needed
442 auto propertyMap = msgData.find("Status");
443 if (propertyMap != msgData.end())
444 {
445 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
446 "POWER_SYS_INPUT_STATUS",
447 std::get<std::string>(propertyMap->second));
448 determineStatusOfPower();
449 return;
450 }
451 return;
452}
453
Matthew Barthbe6efab2022-03-01 13:21:45 -0600454void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600455{
Andrew Geissler58a18012018-01-19 19:36:05 -0800456 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
457 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600458
Matthew Barthbe6efab2022-03-01 13:21:45 -0600459 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600460 method.append("replace");
461
462 this->bus.call_noreply(method);
463
464 return;
465}
466
Josh D. King697474c2017-03-02 11:15:55 -0600467bool Chassis::stateActive(const std::string& target)
468{
Patrick Williams2975e262020-05-13 18:01:09 -0500469 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600470 sdbusplus::message::object_path unitTargetPath;
471
Andrew Geissler58a18012018-01-19 19:36:05 -0800472 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
473 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600474
475 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600476
William A. Kennington III09568ff2018-05-11 00:03:12 -0700477 try
478 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500479 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700480 result.read(unitTargetPath);
481 }
Patrick Williams0a675212021-09-02 09:49:43 -0500482 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700483 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500484 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700485 return false;
486 }
Josh D. King697474c2017-03-02 11:15:55 -0600487
Andrew Geissler58a18012018-01-19 19:36:05 -0800488 method = this->bus.new_method_call(
489 SYSTEMD_SERVICE,
490 static_cast<const std::string&>(unitTargetPath).c_str(),
491 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600492
493 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600494
Anthony Wilson32c532e2018-10-25 21:56:07 -0500495 try
Josh D. King697474c2017-03-02 11:15:55 -0600496 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500497 auto result = this->bus.call(method);
498 result.read(currentState);
499 }
Patrick Williams0a675212021-09-02 09:49:43 -0500500 catch (const sdbusplus::exception::exception& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500501 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500502 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600503 return false;
504 }
505
Patrick Williams37413dc2020-05-13 11:29:54 -0500506 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800507 return currentStateStr == ACTIVE_STATE ||
508 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600509}
510
Patrick Williams8f8ba392017-05-05 15:47:39 -0500511int Chassis::sysStateChange(sdbusplus::message::message& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600512{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600513 sdbusplus::message::object_path newStateObjPath;
514 std::string newStateUnit{};
515 std::string newStateResult{};
516
Andrew Geissler58a18012018-01-19 19:36:05 -0800517 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700518 try
519 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500520 // newStateID is a throwaway that is needed in order to read the
521 // parameters that are useful out of the dbus message
522 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700523 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
524 }
Patrick Williams0a675212021-09-02 09:49:43 -0500525 catch (const sdbusplus::exception::exception& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700526 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500527 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
528 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700529 return 0;
530 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600531
Allen.Wangc0895622022-03-23 15:46:47 +0800532 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800533 (newStateResult == "done") &&
534 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600535 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500536 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600537 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500538 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600539 }
Potin Lai70f36d82022-03-15 10:25:39 +0800540 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800541 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800542 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800543 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500544 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800545 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500546 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600547
548 // Remove temporary file which is utilized for scenarios where the
549 // BMC is rebooted while the chassis power is still on.
550 // This file is used to indicate to chassis related systemd services
551 // that the chassis is already on and they should skip running.
552 // Once the chassis state is back to on we can clear this file.
553 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
554 size++; // null
555 std::unique_ptr<char[]> chassisFile(new char[size]);
556 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
557 if (std::filesystem::exists(chassisFile.get()))
558 {
559 std::filesystem::remove(chassisFile.get());
560 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800561 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600562
563 return 0;
564}
565
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600566Chassis::Transition Chassis::requestedPowerTransition(Transition value)
567{
568
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500569 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
570 "REQ_POWER_TRAN", value);
Potin Lai70f36d82022-03-15 10:25:39 +0800571 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600572 return server::Chassis::requestedPowerTransition(value);
573}
574
575Chassis::PowerState Chassis::currentPowerState(PowerState value)
576{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500577 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500578 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
579 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500580
581 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530582 if (chassisPowerState == PowerState::On)
583 {
584 pohTimer.resetRemaining();
585 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500586 return chassisPowerState;
587}
588
Patrick Williams45a1ed72021-04-30 21:02:43 -0500589uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500590{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500591 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500592 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500593 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500594 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500595 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500596 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500597}
598
Patrick Williams45a1ed72021-04-30 21:02:43 -0500599void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700600{
601 if (ChassisInherit::currentPowerState() == PowerState::On)
602 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500603 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700604 }
605}
606
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500607void Chassis::restorePOHCounter()
608{
609 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800610 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500611 {
612 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500613 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500614 }
615 else
616 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500617 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500618 }
619}
620
Allen.Wangba182f02022-03-23 19:01:53 +0800621fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500622{
Allen.Wangba182f02022-03-23 19:01:53 +0800623 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500624 std::ofstream os(path.c_str(), std::ios::binary);
625 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500626 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500627 return path;
628}
629
Allen.Wangba182f02022-03-23 19:01:53 +0800630bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500631{
Allen.Wangba182f02022-03-23 19:01:53 +0800632 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500633 try
634 {
635 if (fs::exists(path))
636 {
637 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
638 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500639 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500640 return true;
641 }
642 return false;
643 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500644 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500645 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500646 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500647 fs::remove(path);
648 return false;
649 }
650 catch (const fs::filesystem_error& e)
651 {
652 return false;
653 }
654
655 return false;
656}
657
658void Chassis::startPOHCounter()
659{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500660 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
661 fs::create_directories(dir);
662
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500663 try
664 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700665 auto event = sdeventplus::Event::get_default();
666 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
667 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500668 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700669 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500670 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500671 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
672 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500673 phosphor::logging::commit<InternalFailure>();
674 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600675}
676
Matt Spinler9eab9862018-07-11 14:13:52 -0500677void Chassis::serializeStateChangeTime()
678{
Allen.Wangba182f02022-03-23 19:01:53 +0800679 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500680 std::ofstream os(path.c_str(), std::ios::binary);
681 cereal::JSONOutputArchive oarchive(os);
682
683 oarchive(ChassisInherit::lastStateChangeTime(),
684 ChassisInherit::currentPowerState());
685}
686
687bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
688{
Allen.Wangba182f02022-03-23 19:01:53 +0800689 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500690
691 try
692 {
693 if (fs::exists(path))
694 {
695 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
696 cereal::JSONInputArchive iarchive(is);
697 iarchive(time, state);
698 return true;
699 }
700 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500701 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500702 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500703 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500704 fs::remove(path);
705 }
706
707 return false;
708}
709
710void Chassis::restoreChassisStateChangeTime()
711{
712 uint64_t time;
713 PowerState state;
714
715 if (!deserializeStateChangeTime(time, state))
716 {
717 ChassisInherit::lastStateChangeTime(0);
718 }
719 else
720 {
721 ChassisInherit::lastStateChangeTime(time);
722 }
723}
724
725void Chassis::setStateChangeTime()
726{
727 using namespace std::chrono;
728 uint64_t lastTime;
729 PowerState lastState;
730
731 auto now =
732 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
733 .count();
734
735 // If power is on when the BMC is rebooted, this function will get called
736 // because sysStateChange() runs. Since the power state didn't change
737 // in this case, neither should the state change time, so check that
738 // the power state actually did change here.
739 if (deserializeStateChangeTime(lastTime, lastState))
740 {
741 if (lastState == ChassisInherit::currentPowerState())
742 {
743 return;
744 }
745 }
746
747 ChassisInherit::lastStateChangeTime(now);
748 serializeStateChangeTime();
749}
750
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500751bool Chassis::standbyVoltageRegulatorFault()
752{
753 bool regulatorFault = false;
754
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600755 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500756
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600757 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
758
759 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500760 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600761 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500762 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600763
764 if (1 == gpioval)
765 {
766 info("Detected standby voltage regulator fault");
767 regulatorFault = true;
768 }
769
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500770 return regulatorFault;
771}
772
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600773} // namespace manager
774} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700775} // namespace phosphor