blob: ba30c7399301211b980eee9c412c51fc89ad0832 [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";
Andrew Geissler77a91832022-05-11 12:11:03 -040048constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
49 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060050constexpr auto ACTIVE_STATE = "active";
51constexpr auto ACTIVATING_STATE = "activating";
52
Andrew Geissler2cf2a262022-02-02 14:38:41 -060053// Details at https://upower.freedesktop.org/docs/Device.html
54constexpr uint TYPE_UPS = 3;
55constexpr uint STATE_FULLY_CHARGED = 4;
56constexpr uint BATTERY_LVL_FULL = 8;
57
Andrew Geissler58a18012018-01-19 19:36:05 -080058constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
59constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060060constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
61
Josh D. King697474c2017-03-02 11:15:55 -060062constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
63constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
64
Andrew Geissler8b1f8622022-01-28 16:37:07 -060065constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
66constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
67constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
68constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000069constexpr auto POWERSYSINPUTS_INTERFACE =
70 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060071constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
72
Andrew Geissler0029a5d2017-01-24 14:48:35 -060073void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060074{
Andrew Geissler3a30b052019-05-14 15:54:37 -050075 try
76 {
77 auto method = this->bus.new_method_call(
78 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050079 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050080 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050081 catch (const sdbusplus::exception_t& e)
Andrew Geissler3a30b052019-05-14 15:54:37 -050082 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050083 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler3a30b052019-05-14 15:54:37 -050084 elog<InternalFailure>();
85 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060086
Andrew Geissler0029a5d2017-01-24 14:48:35 -060087 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060088}
89
Potin Lai70f36d82022-03-15 10:25:39 +080090void Chassis::createSystemdTargetTable()
91{
92 systemdTargetTable = {
93 // Use the hard off target to ensure we shutdown immediately
94 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
95 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)}};
96}
97
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060098// TODO - Will be rewritten once sdbusplus client bindings are in place
99// and persistent storage design is in place and sdbusplus
100// has read property function
101void Chassis::determineInitialState()
102{
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600103 // Monitor for any properties changed signals on UPower device path
104 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
105 bus,
106 sdbusplus::bus::match::rules::propertiesChangedNamespace(
107 "/org/freedesktop/UPower", UPOWER_INTERFACE),
108 [this](auto& msg) { this->uPowerChangeEvent(msg); });
109
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000110 // Monitor for any properties changed signals on PowerSystemInputs
111 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
112 bus,
113 sdbusplus::bus::match::rules::propertiesChangedNamespace(
114 fmt::format(
115 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
116 POWERSYSINPUTS_INTERFACE),
117 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
118
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600119 determineStatusOfPower();
120
Patrick Williams2975e262020-05-13 18:01:09 -0500121 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800122 auto method = this->bus.new_method_call(
123 "org.openbmc.control.Power", "/org/openbmc/control/power0",
124 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600125
126 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700127 try
128 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700129 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500130 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700131
Patrick Williams37413dc2020-05-13 11:29:54 -0500132 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700133 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500134 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700135 server::Chassis::currentPowerState(PowerState::On);
136 server::Chassis::requestedPowerTransition(Transition::On);
137 return;
138 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500139 else
140 {
141 // The system is off. If we think it should be on then
142 // we probably lost AC while up, so set a new state
143 // change time.
144 uint64_t lastTime;
145 PowerState lastState;
146
147 if (deserializeStateChangeTime(lastTime, lastState))
148 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600149 // If power was on before the BMC reboot and the reboot reason
150 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500151 if (lastState == PowerState::On)
152 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600153 info(
154 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600155
156 // Reset host sensors since system is off now
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -0500157 // Ensure Power Leds are off.
158 startUnit(fmt::format(CHASSIS_BLACKOUT_TGT_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600159
Matt Spinler9eab9862018-07-11 14:13:52 -0500160 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500161 // Generate file indicating AC loss occurred
162 std::string chassisLostPowerFileFmt =
163 fmt::sprintf(CHASSIS_LOST_POWER_FILE, id);
164 fs::create_directories(BASE_FILE_DIR);
165 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
166 std::ofstream outfile(chassisPowerLossFile);
167 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600168
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000169 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600170 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000171 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600172 {
173 if (standbyVoltageRegulatorFault())
174 {
175 report<Regulator>();
176 }
177 else
178 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400179 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600180 }
181 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000182 else
183 {
184 info("Pinhole reset");
185 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500186 }
187 }
188 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700189 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500190 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700191 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700192 // It's acceptable for the pgood state service to not be available
193 // since it will notify us of the pgood state when it comes up.
194 if (e.name() != nullptr &&
195 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
196 {
197 goto fail;
198 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600199
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700200 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500201 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700202 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600203 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700204
205fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500206 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700207 server::Chassis::currentPowerState(PowerState::Off);
208 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600209
210 return;
211}
212
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600213void Chassis::determineStatusOfPower()
214{
Andrew Geissler77a91832022-05-11 12:11:03 -0400215 auto initialPowerStatus = server::Chassis::currentPowerStatus();
216
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400217 bool powerGood = determineStatusOfUPSPower();
218 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000219 {
220 return;
221 }
222
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400223 powerGood = determineStatusOfPSUPower();
224 if (powerGood)
225 {
226 // All checks passed, set power status to good
227 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400228
229 // If power status transitioned from bad to good and chassis power is
230 // off then call Auto Power Restart to see if the system should auto
231 // power on now that power status is good
232 if ((initialPowerStatus != PowerStatus::Good) &&
233 (server::Chassis::currentPowerState() == PowerState::Off))
234 {
235 info("power status transitioned from {START_PWR_STATE} to Good and "
236 "chassis power is off, calling APR",
237 "START_PWR_STATE", initialPowerStatus);
238 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
239 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400240 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000241}
242
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400243bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000244{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600245 // Find all implementations of the UPower interface
246 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
247 MAPPER_INTERFACE, "GetSubTree");
248
249 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
250
251 std::map<std::string, std::map<std::string, std::vector<std::string>>>
252 mapperResponse;
253
254 try
255 {
256 auto mapperResponseMsg = bus.call(mapper);
257 mapperResponseMsg.read(mapperResponse);
258 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500259 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600260 {
261 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
262 throw;
263 }
264
265 if (mapperResponse.empty())
266 {
267 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600268 }
269
270 // Iterate through all returned Upower interfaces and look for UPS's
271 for (const auto& [path, services] : mapperResponse)
272 {
273 for (const auto& serviceIter : services)
274 {
275 const std::string& service = serviceIter.first;
276
277 try
278 {
279 auto method = bus.new_method_call(service.c_str(), path.c_str(),
280 PROPERTY_INTERFACE, "GetAll");
281 method.append(UPOWER_INTERFACE);
282
283 auto response = bus.call(method);
284 using Property = std::string;
285 using Value = std::variant<bool, uint>;
286 using PropertyMap = std::map<Property, Value>;
287 PropertyMap properties;
288 response.read(properties);
289
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600290 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
291 {
292 info("UPower device {OBJ_PATH} is not a UPS device",
293 "OBJ_PATH", path);
294 continue;
295 }
296
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600297 if (std::get<bool>(properties["IsPresent"]) != true)
298 {
299 // There is a UPS detected but it is not officially
300 // "present" yet. Monitor it for state change.
301 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
302 path);
303 continue;
304 }
305
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600306 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
307 {
308 info("UPS is fully charged");
309 }
310 else
311 {
312 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
313 std::get<uint>(properties["State"]));
314 server::Chassis::currentPowerStatus(
315 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400316 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600317 }
318
319 if (std::get<uint>(properties["BatteryLevel"]) ==
320 BATTERY_LVL_FULL)
321 {
322 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600323 // Only one UPS per system, we've found it and it's all
324 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400325 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600326 }
327 else
328 {
329 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
330 "UPS_BAT_LEVEL",
331 std::get<uint>(properties["BatteryLevel"]));
332 server::Chassis::currentPowerStatus(
333 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400334 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600335 }
336 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500337 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600338 {
339 error("Error reading UPS property, error: {ERROR}, "
340 "service: {SERVICE} path: {PATH}",
341 "ERROR", e, "SERVICE", service, "PATH", path);
342 throw;
343 }
344 }
345 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400346 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600347}
348
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400349bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000350{
351 // Find all implementations of the PowerSystemInputs interface
352 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
353 MAPPER_INTERFACE, "GetSubTree");
354
355 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
356
357 std::map<std::string, std::map<std::string, std::vector<std::string>>>
358 mapperResponse;
359
360 try
361 {
362 auto mapperResponseMsg = bus.call(mapper);
363 mapperResponseMsg.read(mapperResponse);
364 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500365 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000366 {
367 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
368 "ERROR", e);
369 throw;
370 }
371
372 for (const auto& [path, services] : mapperResponse)
373 {
374 for (const auto& serviceIter : services)
375 {
376 const std::string& service = serviceIter.first;
377
378 try
379 {
380 auto method = bus.new_method_call(service.c_str(), path.c_str(),
381 PROPERTY_INTERFACE, "GetAll");
382 method.append(POWERSYSINPUTS_INTERFACE);
383
384 auto response = bus.call(method);
385 using Property = std::string;
386 using Value = std::variant<std::string>;
387 using PropertyMap = std::map<Property, Value>;
388 PropertyMap properties;
389 response.read(properties);
390
391 auto statusStr = std::get<std::string>(properties["Status"]);
392 auto status =
393 decoratorServer::PowerSystemInputs::convertStatusFromString(
394 statusStr);
395
396 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
397 {
398 info("Power System Inputs status is in Fault state");
399 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400400 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000401 }
402 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500403 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000404 {
405 error(
406 "Error reading Power System Inputs property, error: {ERROR}, "
407 "service: {SERVICE} path: {PATH}",
408 "ERROR", e, "SERVICE", service, "PATH", path);
409 throw;
410 }
411 }
412 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400413 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000414}
415
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500416void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600417{
418 debug("UPS Property Change Event Triggered");
419 std::string statusInterface;
420 std::map<std::string, std::variant<uint, bool>> msgData;
421 msg.read(statusInterface, msgData);
422
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000423 // If the change is to any of the properties we are interested in, then call
424 // determineStatusOfPower(), which looks at all the power-related
425 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600426 auto propertyMap = msgData.find("IsPresent");
427 if (propertyMap != msgData.end())
428 {
429 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
430 std::get<bool>(propertyMap->second));
431 determineStatusOfPower();
432 return;
433 }
434
435 propertyMap = msgData.find("State");
436 if (propertyMap != msgData.end())
437 {
438 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
439 std::get<uint>(propertyMap->second));
440 determineStatusOfPower();
441 return;
442 }
443
444 propertyMap = msgData.find("BatteryLevel");
445 if (propertyMap != msgData.end())
446 {
447 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
448 std::get<uint>(propertyMap->second));
449 determineStatusOfPower();
450 return;
451 }
452 return;
453}
454
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500455void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000456{
457 debug("Power System Inputs Property Change Event Triggered");
458 std::string statusInterface;
459 std::map<std::string, std::variant<std::string>> msgData;
460 msg.read(statusInterface, msgData);
461
462 // If the change is to any of the properties we are interested in, then call
463 // determineStatusOfPower(), which looks at all the power-related
464 // interfaces, to see if a power status change is needed
465 auto propertyMap = msgData.find("Status");
466 if (propertyMap != msgData.end())
467 {
468 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
469 "POWER_SYS_INPUT_STATUS",
470 std::get<std::string>(propertyMap->second));
471 determineStatusOfPower();
472 return;
473 }
474 return;
475}
476
Matthew Barthbe6efab2022-03-01 13:21:45 -0600477void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600478{
Andrew Geissler58a18012018-01-19 19:36:05 -0800479 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
480 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600481
Matthew Barthbe6efab2022-03-01 13:21:45 -0600482 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600483 method.append("replace");
484
485 this->bus.call_noreply(method);
486
487 return;
488}
489
Andrew Geissler77a91832022-05-11 12:11:03 -0400490void Chassis::restartUnit(const std::string& sysdUnit)
491{
492 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
493 SYSTEMD_INTERFACE, "RestartUnit");
494
495 method.append(sysdUnit);
496 method.append("replace");
497
498 this->bus.call_noreply(method);
499
500 return;
501}
502
Josh D. King697474c2017-03-02 11:15:55 -0600503bool Chassis::stateActive(const std::string& target)
504{
Patrick Williams2975e262020-05-13 18:01:09 -0500505 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600506 sdbusplus::message::object_path unitTargetPath;
507
Andrew Geissler58a18012018-01-19 19:36:05 -0800508 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
509 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600510
511 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600512
William A. Kennington III09568ff2018-05-11 00:03:12 -0700513 try
514 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500515 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700516 result.read(unitTargetPath);
517 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500518 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700519 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500520 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700521 return false;
522 }
Josh D. King697474c2017-03-02 11:15:55 -0600523
Andrew Geissler58a18012018-01-19 19:36:05 -0800524 method = this->bus.new_method_call(
525 SYSTEMD_SERVICE,
526 static_cast<const std::string&>(unitTargetPath).c_str(),
527 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600528
529 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600530
Anthony Wilson32c532e2018-10-25 21:56:07 -0500531 try
Josh D. King697474c2017-03-02 11:15:55 -0600532 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500533 auto result = this->bus.call(method);
534 result.read(currentState);
535 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500536 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500537 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500538 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600539 return false;
540 }
541
Patrick Williams37413dc2020-05-13 11:29:54 -0500542 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800543 return currentStateStr == ACTIVE_STATE ||
544 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600545}
546
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500547int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600548{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600549 sdbusplus::message::object_path newStateObjPath;
550 std::string newStateUnit{};
551 std::string newStateResult{};
552
Andrew Geissler58a18012018-01-19 19:36:05 -0800553 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700554 try
555 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500556 // newStateID is a throwaway that is needed in order to read the
557 // parameters that are useful out of the dbus message
558 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700559 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
560 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500561 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700562 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500563 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
564 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700565 return 0;
566 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600567
Allen.Wangc0895622022-03-23 15:46:47 +0800568 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800569 (newStateResult == "done") &&
570 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600571 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500572 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600573 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500574 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600575 }
Potin Lai70f36d82022-03-15 10:25:39 +0800576 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800577 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800578 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800579 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500580 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800581 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500582 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600583
584 // Remove temporary file which is utilized for scenarios where the
585 // BMC is rebooted while the chassis power is still on.
586 // This file is used to indicate to chassis related systemd services
587 // that the chassis is already on and they should skip running.
588 // Once the chassis state is back to on we can clear this file.
589 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
590 size++; // null
591 std::unique_ptr<char[]> chassisFile(new char[size]);
592 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
593 if (std::filesystem::exists(chassisFile.get()))
594 {
595 std::filesystem::remove(chassisFile.get());
596 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800597 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600598
599 return 0;
600}
601
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600602Chassis::Transition Chassis::requestedPowerTransition(Transition value)
603{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500604 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
605 "REQ_POWER_TRAN", value);
Potin Lai70f36d82022-03-15 10:25:39 +0800606 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600607 return server::Chassis::requestedPowerTransition(value);
608}
609
610Chassis::PowerState Chassis::currentPowerState(PowerState value)
611{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500612 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500613 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
614 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500615
616 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530617 if (chassisPowerState == PowerState::On)
618 {
619 pohTimer.resetRemaining();
620 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500621 return chassisPowerState;
622}
623
Patrick Williams45a1ed72021-04-30 21:02:43 -0500624uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500625{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500626 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500627 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500628 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500629 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500630 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500631 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500632}
633
Patrick Williams45a1ed72021-04-30 21:02:43 -0500634void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700635{
636 if (ChassisInherit::currentPowerState() == PowerState::On)
637 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500638 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700639 }
640}
641
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500642void Chassis::restorePOHCounter()
643{
644 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800645 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500646 {
647 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500648 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500649 }
650 else
651 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500652 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500653 }
654}
655
Allen.Wangba182f02022-03-23 19:01:53 +0800656fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500657{
Allen.Wangba182f02022-03-23 19:01:53 +0800658 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500659 std::ofstream os(path.c_str(), std::ios::binary);
660 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500661 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500662 return path;
663}
664
Allen.Wangba182f02022-03-23 19:01:53 +0800665bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500666{
Allen.Wangba182f02022-03-23 19:01:53 +0800667 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500668 try
669 {
670 if (fs::exists(path))
671 {
672 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
673 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500674 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500675 return true;
676 }
677 return false;
678 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500679 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500680 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500681 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500682 fs::remove(path);
683 return false;
684 }
685 catch (const fs::filesystem_error& e)
686 {
687 return false;
688 }
689
690 return false;
691}
692
693void Chassis::startPOHCounter()
694{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500695 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
696 fs::create_directories(dir);
697
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500698 try
699 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700700 auto event = sdeventplus::Event::get_default();
701 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
702 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500703 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700704 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500705 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500706 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
707 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500708 phosphor::logging::commit<InternalFailure>();
709 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600710}
711
Matt Spinler9eab9862018-07-11 14:13:52 -0500712void Chassis::serializeStateChangeTime()
713{
Allen.Wangba182f02022-03-23 19:01:53 +0800714 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500715 std::ofstream os(path.c_str(), std::ios::binary);
716 cereal::JSONOutputArchive oarchive(os);
717
718 oarchive(ChassisInherit::lastStateChangeTime(),
719 ChassisInherit::currentPowerState());
720}
721
722bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
723{
Allen.Wangba182f02022-03-23 19:01:53 +0800724 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500725
726 try
727 {
728 if (fs::exists(path))
729 {
730 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
731 cereal::JSONInputArchive iarchive(is);
732 iarchive(time, state);
733 return true;
734 }
735 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500736 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500737 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500738 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500739 fs::remove(path);
740 }
741
742 return false;
743}
744
745void Chassis::restoreChassisStateChangeTime()
746{
747 uint64_t time;
748 PowerState state;
749
750 if (!deserializeStateChangeTime(time, state))
751 {
752 ChassisInherit::lastStateChangeTime(0);
753 }
754 else
755 {
756 ChassisInherit::lastStateChangeTime(time);
757 }
758}
759
760void Chassis::setStateChangeTime()
761{
762 using namespace std::chrono;
763 uint64_t lastTime;
764 PowerState lastState;
765
766 auto now =
767 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
768 .count();
769
770 // If power is on when the BMC is rebooted, this function will get called
771 // because sysStateChange() runs. Since the power state didn't change
772 // in this case, neither should the state change time, so check that
773 // the power state actually did change here.
774 if (deserializeStateChangeTime(lastTime, lastState))
775 {
776 if (lastState == ChassisInherit::currentPowerState())
777 {
778 return;
779 }
780 }
781
782 ChassisInherit::lastStateChangeTime(now);
783 serializeStateChangeTime();
784}
785
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500786bool Chassis::standbyVoltageRegulatorFault()
787{
788 bool regulatorFault = false;
789
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600790 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500791
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600792 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
793
794 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500795 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600796 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500797 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600798
799 if (1 == gpioval)
800 {
801 info("Detected standby voltage regulator fault");
802 regulatorFault = true;
803 }
804
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500805 return regulatorFault;
806}
807
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600808} // namespace manager
809} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700810} // namespace phosphor