blob: db5ae96145a0d98e4cb702892590fda82a96f75a [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>
NodeMan97bcbee4a2022-05-27 15:28:45 -050010#include <fmt/printf.h>
Potin Lai70f36d82022-03-15 10:25:39 +080011
Andrew Geisslere426b582020-05-28 12:40:55 -050012#include <cereal/archives/json.hpp>
13#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050014#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060015#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070016#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070017#include <sdeventplus/event.hpp>
18#include <sdeventplus/exception.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000019#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050020
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060021#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050022#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050023
Andrew Geisslera90a31a2016-12-13 16:16:28 -060024namespace phosphor
25{
26namespace state
27{
28namespace manager
29{
30
Andrew Geissler8ffdb262021-09-20 15:25:19 -050031PHOSPHOR_LOG2_USING;
32
Andrew Geisslera90a31a2016-12-13 16:16:28 -060033// When you see server:: you know we're referencing our base class
34namespace server = sdbusplus::xyz::openbmc_project::State::server;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000035namespace decoratorServer =
36 sdbusplus::xyz::openbmc_project::State::Decorator::server;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060037
38using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070039using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050040using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050041using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080042constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
43 "obmc-chassis-poweroff@{}.target";
44constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
45 "obmc-chassis-hard-poweroff@{}.target";
46constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -050047constexpr auto CHASSIS_BLACKOUT_TGT_FMT = "obmc-chassis-blackout@{}.target";
DelphineCCChiu06881322022-11-09 08:41:25 +080048constexpr auto CHASSIS_STATE_POWERCYCLE_TGT_FMT =
49 "obmc-chassis-powercycle@{}.target";
Andrew Geissler77a91832022-05-11 12:11:03 -040050constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
51 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060052constexpr auto ACTIVE_STATE = "active";
53constexpr auto ACTIVATING_STATE = "activating";
54
Andrew Geissler2cf2a262022-02-02 14:38:41 -060055// Details at https://upower.freedesktop.org/docs/Device.html
56constexpr uint TYPE_UPS = 3;
57constexpr uint STATE_FULLY_CHARGED = 4;
58constexpr uint BATTERY_LVL_FULL = 8;
59
Andrew Geissler58a18012018-01-19 19:36:05 -080060constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
61constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060062constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
63
Josh D. King697474c2017-03-02 11:15:55 -060064constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
65constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
66
Andrew Geissler8b1f8622022-01-28 16:37:07 -060067constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
68constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
69constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
70constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000071constexpr auto POWERSYSINPUTS_INTERFACE =
72 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060073constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
74
Andrew Geissler0029a5d2017-01-24 14:48:35 -060075void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060076{
Andrew Geissler3a30b052019-05-14 15:54:37 -050077 try
78 {
79 auto method = this->bus.new_method_call(
80 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050081 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050082 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050083 catch (const sdbusplus::exception_t& e)
Andrew Geissler3a30b052019-05-14 15:54:37 -050084 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050085 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler3a30b052019-05-14 15:54:37 -050086 elog<InternalFailure>();
87 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060088
Andrew Geissler0029a5d2017-01-24 14:48:35 -060089 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060090}
91
Potin Lai70f36d82022-03-15 10:25:39 +080092void Chassis::createSystemdTargetTable()
93{
94 systemdTargetTable = {
95 // Use the hard off target to ensure we shutdown immediately
96 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
DelphineCCChiu06881322022-11-09 08:41:25 +080097 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)},
98 {Transition::PowerCycle,
99 fmt::format(CHASSIS_STATE_POWERCYCLE_TGT_FMT, id)}};
Potin Lai70f36d82022-03-15 10:25:39 +0800100}
101
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600102// TODO - Will be rewritten once sdbusplus client bindings are in place
103// and persistent storage design is in place and sdbusplus
104// has read property function
105void Chassis::determineInitialState()
106{
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600107 // Monitor for any properties changed signals on UPower device path
108 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
109 bus,
110 sdbusplus::bus::match::rules::propertiesChangedNamespace(
111 "/org/freedesktop/UPower", UPOWER_INTERFACE),
112 [this](auto& msg) { this->uPowerChangeEvent(msg); });
113
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000114 // Monitor for any properties changed signals on PowerSystemInputs
115 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
116 bus,
117 sdbusplus::bus::match::rules::propertiesChangedNamespace(
118 fmt::format(
119 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
120 POWERSYSINPUTS_INTERFACE),
121 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
122
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600123 determineStatusOfPower();
124
Patrick Williams2975e262020-05-13 18:01:09 -0500125 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800126 auto method = this->bus.new_method_call(
127 "org.openbmc.control.Power", "/org/openbmc/control/power0",
128 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600129
130 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700131 try
132 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700133 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500134 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700135
Patrick Williams37413dc2020-05-13 11:29:54 -0500136 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700137 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500138 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700139 server::Chassis::currentPowerState(PowerState::On);
140 server::Chassis::requestedPowerTransition(Transition::On);
141 return;
142 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500143 else
144 {
145 // The system is off. If we think it should be on then
146 // we probably lost AC while up, so set a new state
147 // change time.
148 uint64_t lastTime;
149 PowerState lastState;
150
151 if (deserializeStateChangeTime(lastTime, lastState))
152 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600153 // If power was on before the BMC reboot and the reboot reason
154 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500155 if (lastState == PowerState::On)
156 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600157 info(
158 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600159
160 // Reset host sensors since system is off now
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -0500161 // Ensure Power Leds are off.
162 startUnit(fmt::format(CHASSIS_BLACKOUT_TGT_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600163
Matt Spinler9eab9862018-07-11 14:13:52 -0500164 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500165 // Generate file indicating AC loss occurred
166 std::string chassisLostPowerFileFmt =
167 fmt::sprintf(CHASSIS_LOST_POWER_FILE, id);
168 fs::create_directories(BASE_FILE_DIR);
169 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
170 std::ofstream outfile(chassisPowerLossFile);
171 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600172
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000173 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600174 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000175 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600176 {
177 if (standbyVoltageRegulatorFault())
178 {
179 report<Regulator>();
180 }
181 else
182 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400183 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600184 }
185 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000186 else
187 {
188 info("Pinhole reset");
189 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500190 }
191 }
192 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700193 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500194 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700195 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700196 // It's acceptable for the pgood state service to not be available
197 // since it will notify us of the pgood state when it comes up.
198 if (e.name() != nullptr &&
199 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
200 {
201 goto fail;
202 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600203
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700204 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500205 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700206 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600207 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700208
209fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500210 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700211 server::Chassis::currentPowerState(PowerState::Off);
212 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600213
214 return;
215}
216
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600217void Chassis::determineStatusOfPower()
218{
Andrew Geissler77a91832022-05-11 12:11:03 -0400219 auto initialPowerStatus = server::Chassis::currentPowerStatus();
220
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400221 bool powerGood = determineStatusOfUPSPower();
222 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000223 {
224 return;
225 }
226
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400227 powerGood = determineStatusOfPSUPower();
228 if (powerGood)
229 {
230 // All checks passed, set power status to good
231 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400232
233 // If power status transitioned from bad to good and chassis power is
234 // off then call Auto Power Restart to see if the system should auto
235 // power on now that power status is good
236 if ((initialPowerStatus != PowerStatus::Good) &&
237 (server::Chassis::currentPowerState() == PowerState::Off))
238 {
239 info("power status transitioned from {START_PWR_STATE} to Good and "
240 "chassis power is off, calling APR",
241 "START_PWR_STATE", initialPowerStatus);
242 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
243 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400244 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000245}
246
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400247bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000248{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600249 // Find all implementations of the UPower interface
250 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
251 MAPPER_INTERFACE, "GetSubTree");
252
253 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
254
255 std::map<std::string, std::map<std::string, std::vector<std::string>>>
256 mapperResponse;
257
258 try
259 {
260 auto mapperResponseMsg = bus.call(mapper);
261 mapperResponseMsg.read(mapperResponse);
262 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500263 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600264 {
265 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
266 throw;
267 }
268
269 if (mapperResponse.empty())
270 {
271 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600272 }
273
274 // Iterate through all returned Upower interfaces and look for UPS's
275 for (const auto& [path, services] : mapperResponse)
276 {
277 for (const auto& serviceIter : services)
278 {
279 const std::string& service = serviceIter.first;
280
281 try
282 {
283 auto method = bus.new_method_call(service.c_str(), path.c_str(),
284 PROPERTY_INTERFACE, "GetAll");
285 method.append(UPOWER_INTERFACE);
286
287 auto response = bus.call(method);
288 using Property = std::string;
289 using Value = std::variant<bool, uint>;
290 using PropertyMap = std::map<Property, Value>;
291 PropertyMap properties;
292 response.read(properties);
293
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600294 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
295 {
296 info("UPower device {OBJ_PATH} is not a UPS device",
297 "OBJ_PATH", path);
298 continue;
299 }
300
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600301 if (std::get<bool>(properties["IsPresent"]) != true)
302 {
303 // There is a UPS detected but it is not officially
304 // "present" yet. Monitor it for state change.
305 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
306 path);
307 continue;
308 }
309
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600310 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
311 {
312 info("UPS is fully charged");
313 }
314 else
315 {
316 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
317 std::get<uint>(properties["State"]));
318 server::Chassis::currentPowerStatus(
319 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400320 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600321 }
322
323 if (std::get<uint>(properties["BatteryLevel"]) ==
324 BATTERY_LVL_FULL)
325 {
326 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600327 // Only one UPS per system, we've found it and it's all
328 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400329 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600330 }
331 else
332 {
333 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
334 "UPS_BAT_LEVEL",
335 std::get<uint>(properties["BatteryLevel"]));
336 server::Chassis::currentPowerStatus(
337 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400338 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600339 }
340 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500341 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600342 {
343 error("Error reading UPS property, error: {ERROR}, "
344 "service: {SERVICE} path: {PATH}",
345 "ERROR", e, "SERVICE", service, "PATH", path);
346 throw;
347 }
348 }
349 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400350 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600351}
352
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400353bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000354{
355 // Find all implementations of the PowerSystemInputs interface
356 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
357 MAPPER_INTERFACE, "GetSubTree");
358
359 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
360
361 std::map<std::string, std::map<std::string, std::vector<std::string>>>
362 mapperResponse;
363
364 try
365 {
366 auto mapperResponseMsg = bus.call(mapper);
367 mapperResponseMsg.read(mapperResponse);
368 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500369 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000370 {
371 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
372 "ERROR", e);
373 throw;
374 }
375
376 for (const auto& [path, services] : mapperResponse)
377 {
378 for (const auto& serviceIter : services)
379 {
380 const std::string& service = serviceIter.first;
381
382 try
383 {
384 auto method = bus.new_method_call(service.c_str(), path.c_str(),
385 PROPERTY_INTERFACE, "GetAll");
386 method.append(POWERSYSINPUTS_INTERFACE);
387
388 auto response = bus.call(method);
389 using Property = std::string;
390 using Value = std::variant<std::string>;
391 using PropertyMap = std::map<Property, Value>;
392 PropertyMap properties;
393 response.read(properties);
394
395 auto statusStr = std::get<std::string>(properties["Status"]);
396 auto status =
397 decoratorServer::PowerSystemInputs::convertStatusFromString(
398 statusStr);
399
400 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
401 {
402 info("Power System Inputs status is in Fault state");
403 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400404 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000405 }
406 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500407 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000408 {
409 error(
410 "Error reading Power System Inputs property, error: {ERROR}, "
411 "service: {SERVICE} path: {PATH}",
412 "ERROR", e, "SERVICE", service, "PATH", path);
413 throw;
414 }
415 }
416 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400417 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000418}
419
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500420void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600421{
422 debug("UPS Property Change Event Triggered");
423 std::string statusInterface;
424 std::map<std::string, std::variant<uint, bool>> msgData;
425 msg.read(statusInterface, msgData);
426
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000427 // If the change is to any of the properties we are interested in, then call
428 // determineStatusOfPower(), which looks at all the power-related
429 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600430 auto propertyMap = msgData.find("IsPresent");
431 if (propertyMap != msgData.end())
432 {
433 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
434 std::get<bool>(propertyMap->second));
435 determineStatusOfPower();
436 return;
437 }
438
439 propertyMap = msgData.find("State");
440 if (propertyMap != msgData.end())
441 {
442 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
443 std::get<uint>(propertyMap->second));
444 determineStatusOfPower();
445 return;
446 }
447
448 propertyMap = msgData.find("BatteryLevel");
449 if (propertyMap != msgData.end())
450 {
451 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
452 std::get<uint>(propertyMap->second));
453 determineStatusOfPower();
454 return;
455 }
456 return;
457}
458
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500459void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000460{
461 debug("Power System Inputs Property Change Event Triggered");
462 std::string statusInterface;
463 std::map<std::string, std::variant<std::string>> msgData;
464 msg.read(statusInterface, msgData);
465
466 // If the change is to any of the properties we are interested in, then call
467 // determineStatusOfPower(), which looks at all the power-related
468 // interfaces, to see if a power status change is needed
469 auto propertyMap = msgData.find("Status");
470 if (propertyMap != msgData.end())
471 {
472 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
473 "POWER_SYS_INPUT_STATUS",
474 std::get<std::string>(propertyMap->second));
475 determineStatusOfPower();
476 return;
477 }
478 return;
479}
480
Matthew Barthbe6efab2022-03-01 13:21:45 -0600481void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600482{
Andrew Geissler58a18012018-01-19 19:36:05 -0800483 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
484 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600485
Matthew Barthbe6efab2022-03-01 13:21:45 -0600486 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600487 method.append("replace");
488
489 this->bus.call_noreply(method);
490
491 return;
492}
493
Andrew Geissler77a91832022-05-11 12:11:03 -0400494void Chassis::restartUnit(const std::string& sysdUnit)
495{
496 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
497 SYSTEMD_INTERFACE, "RestartUnit");
498
499 method.append(sysdUnit);
500 method.append("replace");
501
502 this->bus.call_noreply(method);
503
504 return;
505}
506
Josh D. King697474c2017-03-02 11:15:55 -0600507bool Chassis::stateActive(const std::string& target)
508{
Patrick Williams2975e262020-05-13 18:01:09 -0500509 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600510 sdbusplus::message::object_path unitTargetPath;
511
Andrew Geissler58a18012018-01-19 19:36:05 -0800512 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
513 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600514
515 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600516
William A. Kennington III09568ff2018-05-11 00:03:12 -0700517 try
518 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500519 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700520 result.read(unitTargetPath);
521 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500522 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700523 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500524 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700525 return false;
526 }
Josh D. King697474c2017-03-02 11:15:55 -0600527
Andrew Geissler58a18012018-01-19 19:36:05 -0800528 method = this->bus.new_method_call(
529 SYSTEMD_SERVICE,
530 static_cast<const std::string&>(unitTargetPath).c_str(),
531 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600532
533 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600534
Anthony Wilson32c532e2018-10-25 21:56:07 -0500535 try
Josh D. King697474c2017-03-02 11:15:55 -0600536 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500537 auto result = this->bus.call(method);
538 result.read(currentState);
539 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500540 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500541 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500542 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600543 return false;
544 }
545
Patrick Williams37413dc2020-05-13 11:29:54 -0500546 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800547 return currentStateStr == ACTIVE_STATE ||
548 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600549}
550
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500551int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600552{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600553 sdbusplus::message::object_path newStateObjPath;
554 std::string newStateUnit{};
555 std::string newStateResult{};
556
Andrew Geissler58a18012018-01-19 19:36:05 -0800557 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700558 try
559 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500560 // newStateID is a throwaway that is needed in order to read the
561 // parameters that are useful out of the dbus message
562 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700563 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
564 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500565 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700566 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500567 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
568 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700569 return 0;
570 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600571
Allen.Wangc0895622022-03-23 15:46:47 +0800572 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800573 (newStateResult == "done") &&
574 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600575 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500576 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600577 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500578 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600579 }
Potin Lai70f36d82022-03-15 10:25:39 +0800580 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800581 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800582 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800583 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500584 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800585 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500586 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600587
588 // Remove temporary file which is utilized for scenarios where the
589 // BMC is rebooted while the chassis power is still on.
590 // This file is used to indicate to chassis related systemd services
591 // that the chassis is already on and they should skip running.
592 // Once the chassis state is back to on we can clear this file.
593 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
594 size++; // null
595 std::unique_ptr<char[]> chassisFile(new char[size]);
596 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
597 if (std::filesystem::exists(chassisFile.get()))
598 {
599 std::filesystem::remove(chassisFile.get());
600 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800601 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600602
603 return 0;
604}
605
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600606Chassis::Transition Chassis::requestedPowerTransition(Transition value)
607{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500608 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
609 "REQ_POWER_TRAN", value);
Potin Lai70f36d82022-03-15 10:25:39 +0800610 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600611 return server::Chassis::requestedPowerTransition(value);
612}
613
614Chassis::PowerState Chassis::currentPowerState(PowerState value)
615{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500616 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500617 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
618 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500619
620 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530621 if (chassisPowerState == PowerState::On)
622 {
623 pohTimer.resetRemaining();
624 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500625 return chassisPowerState;
626}
627
Patrick Williams45a1ed72021-04-30 21:02:43 -0500628uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500629{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500630 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500631 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500632 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500633 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500634 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500635 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500636}
637
Patrick Williams45a1ed72021-04-30 21:02:43 -0500638void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700639{
640 if (ChassisInherit::currentPowerState() == PowerState::On)
641 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500642 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700643 }
644}
645
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500646void Chassis::restorePOHCounter()
647{
648 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800649 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500650 {
651 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500652 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500653 }
654 else
655 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500656 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500657 }
658}
659
Allen.Wangba182f02022-03-23 19:01:53 +0800660fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500661{
Allen.Wangba182f02022-03-23 19:01:53 +0800662 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500663 std::ofstream os(path.c_str(), std::ios::binary);
664 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500665 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500666 return path;
667}
668
Allen.Wangba182f02022-03-23 19:01:53 +0800669bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500670{
Allen.Wangba182f02022-03-23 19:01:53 +0800671 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500672 try
673 {
674 if (fs::exists(path))
675 {
676 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
677 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500678 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500679 return true;
680 }
681 return false;
682 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500683 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500684 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500685 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500686 fs::remove(path);
687 return false;
688 }
689 catch (const fs::filesystem_error& e)
690 {
691 return false;
692 }
693
694 return false;
695}
696
697void Chassis::startPOHCounter()
698{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500699 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
700 fs::create_directories(dir);
701
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500702 try
703 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700704 auto event = sdeventplus::Event::get_default();
705 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
706 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500707 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700708 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500709 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500710 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
711 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500712 phosphor::logging::commit<InternalFailure>();
713 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600714}
715
Matt Spinler9eab9862018-07-11 14:13:52 -0500716void Chassis::serializeStateChangeTime()
717{
Allen.Wangba182f02022-03-23 19:01:53 +0800718 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500719 std::ofstream os(path.c_str(), std::ios::binary);
720 cereal::JSONOutputArchive oarchive(os);
721
722 oarchive(ChassisInherit::lastStateChangeTime(),
723 ChassisInherit::currentPowerState());
724}
725
726bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
727{
Allen.Wangba182f02022-03-23 19:01:53 +0800728 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500729
730 try
731 {
732 if (fs::exists(path))
733 {
734 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
735 cereal::JSONInputArchive iarchive(is);
736 iarchive(time, state);
737 return true;
738 }
739 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500740 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500741 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500742 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500743 fs::remove(path);
744 }
745
746 return false;
747}
748
749void Chassis::restoreChassisStateChangeTime()
750{
751 uint64_t time;
752 PowerState state;
753
754 if (!deserializeStateChangeTime(time, state))
755 {
756 ChassisInherit::lastStateChangeTime(0);
757 }
758 else
759 {
760 ChassisInherit::lastStateChangeTime(time);
761 }
762}
763
764void Chassis::setStateChangeTime()
765{
766 using namespace std::chrono;
767 uint64_t lastTime;
768 PowerState lastState;
769
770 auto now =
771 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
772 .count();
773
774 // If power is on when the BMC is rebooted, this function will get called
775 // because sysStateChange() runs. Since the power state didn't change
776 // in this case, neither should the state change time, so check that
777 // the power state actually did change here.
778 if (deserializeStateChangeTime(lastTime, lastState))
779 {
780 if (lastState == ChassisInherit::currentPowerState())
781 {
782 return;
783 }
784 }
785
786 ChassisInherit::lastStateChangeTime(now);
787 serializeStateChangeTime();
788}
789
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500790bool Chassis::standbyVoltageRegulatorFault()
791{
792 bool regulatorFault = false;
793
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600794 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500795
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600796 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
797
798 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500799 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600800 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500801 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600802
803 if (1 == gpioval)
804 {
805 info("Detected standby voltage regulator fault");
806 regulatorFault = true;
807 }
808
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500809 return regulatorFault;
810}
811
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600812} // namespace manager
813} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700814} // namespace phosphor