blob: 05cc784686a51cbff2e2728113659c3c8fe0f087 [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
9#include <cereal/archives/json.hpp>
Patrick Williams9a286db2024-01-17 06:29:47 -060010#include <org/freedesktop/UPower/Device/client.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050011#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050012#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060013#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070014#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070015#include <sdeventplus/event.hpp>
16#include <sdeventplus/exception.hpp>
Patrick Williams9a286db2024-01-17 06:29:47 -060017#include <xyz/openbmc_project/ObjectMapper/client.hpp>
Andrew Geissler765446a2023-05-25 15:38:20 -040018#include <xyz/openbmc_project/State/Chassis/error.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>
Patrick Williams78c066f2024-02-13 12:25:58 -060022#include <format>
Andrew Geisslere426b582020-05-28 12:40:55 -050023#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050024
Andrew Geisslera90a31a2016-12-13 16:16:28 -060025namespace phosphor
26{
27namespace state
28{
29namespace manager
30{
31
Andrew Geissler8ffdb262021-09-20 15:25:19 -050032PHOSPHOR_LOG2_USING;
33
Andrew Geisslera90a31a2016-12-13 16:16:28 -060034// When you see server:: you know we're referencing our base class
Patrick Williams7e969cb2023-08-23 16:24:23 -050035namespace server = sdbusplus::server::xyz::openbmc_project::state;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000036namespace decoratorServer =
Patrick Williams7e969cb2023-08-23 16:24:23 -050037 sdbusplus::server::xyz::openbmc_project::state::decorator;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060038
Patrick Williams9a286db2024-01-17 06:29:47 -060039using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
40using UPowerDevice = sdbusplus::client::org::freedesktop::u_power::Device<>;
41
Andrew Geisslera90a31a2016-12-13 16:16:28 -060042using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070043using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050044using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050045using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080046constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
47 "obmc-chassis-poweroff@{}.target";
48constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
49 "obmc-chassis-hard-poweroff@{}.target";
50constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -050051constexpr auto CHASSIS_BLACKOUT_TGT_FMT = "obmc-chassis-blackout@{}.target";
DelphineCCChiu06881322022-11-09 08:41:25 +080052constexpr auto CHASSIS_STATE_POWERCYCLE_TGT_FMT =
53 "obmc-chassis-powercycle@{}.target";
Andrew Geissler77a91832022-05-11 12:11:03 -040054constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
55 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060056constexpr auto ACTIVE_STATE = "active";
57constexpr auto ACTIVATING_STATE = "activating";
58
Andrew Geissler2cf2a262022-02-02 14:38:41 -060059// Details at https://upower.freedesktop.org/docs/Device.html
60constexpr uint TYPE_UPS = 3;
61constexpr uint STATE_FULLY_CHARGED = 4;
62constexpr uint BATTERY_LVL_FULL = 8;
63
Andrew Geissler58a18012018-01-19 19:36:05 -080064constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
65constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060066constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
67
Josh D. King697474c2017-03-02 11:15:55 -060068constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
69constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
70
Andrew Geissler8b1f8622022-01-28 16:37:07 -060071constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
72
Potin Lai70f36d82022-03-15 10:25:39 +080073void Chassis::createSystemdTargetTable()
74{
75 systemdTargetTable = {
76 // Use the hard off target to ensure we shutdown immediately
Patrick Williams78c066f2024-02-13 12:25:58 -060077 {Transition::Off, std::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
78 {Transition::On, std::format(CHASSIS_STATE_POWERON_TGT_FMT, id)},
DelphineCCChiu06881322022-11-09 08:41:25 +080079 {Transition::PowerCycle,
Patrick Williams78c066f2024-02-13 12:25:58 -060080 std::format(CHASSIS_STATE_POWERCYCLE_TGT_FMT, id)}};
Potin Lai70f36d82022-03-15 10:25:39 +080081}
82
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060083// TODO - Will be rewritten once sdbusplus client bindings are in place
84// and persistent storage design is in place and sdbusplus
85// has read property function
86void Chassis::determineInitialState()
87{
Andrew Geissler2cf2a262022-02-02 14:38:41 -060088 // Monitor for any properties changed signals on UPower device path
89 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
90 bus,
91 sdbusplus::bus::match::rules::propertiesChangedNamespace(
Patrick Williams9a286db2024-01-17 06:29:47 -060092 "/org/freedesktop/UPower", UPowerDevice::interface),
Andrew Geissler2cf2a262022-02-02 14:38:41 -060093 [this](auto& msg) { this->uPowerChangeEvent(msg); });
94
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000095 // Monitor for any properties changed signals on PowerSystemInputs
96 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
97 bus,
98 sdbusplus::bus::match::rules::propertiesChangedNamespace(
Patrick Williams78c066f2024-02-13 12:25:58 -060099 std::format(
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000100 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
Patrick Williams9a286db2024-01-17 06:29:47 -0600101 decoratorServer::PowerSystemInputs::interface),
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000102 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
103
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600104 determineStatusOfPower();
105
Patrick Williams2975e262020-05-13 18:01:09 -0500106 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800107 auto method = this->bus.new_method_call(
108 "org.openbmc.control.Power", "/org/openbmc/control/power0",
109 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600110
111 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700112 try
113 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700114 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500115 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700116
Patrick Williams37413dc2020-05-13 11:29:54 -0500117 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700118 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500119 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700120 server::Chassis::currentPowerState(PowerState::On);
121 server::Chassis::requestedPowerTransition(Transition::On);
122 return;
123 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500124 else
125 {
126 // The system is off. If we think it should be on then
127 // we probably lost AC while up, so set a new state
128 // change time.
129 uint64_t lastTime;
130 PowerState lastState;
131
132 if (deserializeStateChangeTime(lastTime, lastState))
133 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600134 // If power was on before the BMC reboot and the reboot reason
135 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500136 if (lastState == PowerState::On)
137 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600138 info(
139 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600140
141 // Reset host sensors since system is off now
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -0500142 // Ensure Power Leds are off.
Patrick Williams78c066f2024-02-13 12:25:58 -0600143 startUnit(std::format(CHASSIS_BLACKOUT_TGT_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600144
Matt Spinler9eab9862018-07-11 14:13:52 -0500145 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500146 // Generate file indicating AC loss occurred
147 std::string chassisLostPowerFileFmt =
Patrick Williams78c066f2024-02-13 12:25:58 -0600148 std::format(CHASSIS_LOST_POWER_FILE, id);
NodeMan97bcbee4a2022-05-27 15:28:45 -0500149 fs::create_directories(BASE_FILE_DIR);
150 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
151 std::ofstream outfile(chassisPowerLossFile);
152 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600153
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000154 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600155 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000156 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600157 {
158 if (standbyVoltageRegulatorFault())
159 {
160 report<Regulator>();
161 }
162 else
163 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400164 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600165 }
166 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000167 else
168 {
169 info("Pinhole reset");
170 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500171 }
172 }
173 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700174 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500175 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700176 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700177 // It's acceptable for the pgood state service to not be available
178 // since it will notify us of the pgood state when it comes up.
179 if (e.name() != nullptr &&
180 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
181 {
182 goto fail;
183 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600184
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700185 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500186 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700187 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600188 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700189
190fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500191 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700192 server::Chassis::currentPowerState(PowerState::Off);
193 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600194
195 return;
196}
197
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600198void Chassis::determineStatusOfPower()
199{
Andrew Geissler77a91832022-05-11 12:11:03 -0400200 auto initialPowerStatus = server::Chassis::currentPowerStatus();
201
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400202 bool powerGood = determineStatusOfUPSPower();
203 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000204 {
205 return;
206 }
207
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400208 powerGood = determineStatusOfPSUPower();
209 if (powerGood)
210 {
211 // All checks passed, set power status to good
212 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400213
214 // If power status transitioned from bad to good and chassis power is
215 // off then call Auto Power Restart to see if the system should auto
216 // power on now that power status is good
217 if ((initialPowerStatus != PowerStatus::Good) &&
218 (server::Chassis::currentPowerState() == PowerState::Off))
219 {
220 info("power status transitioned from {START_PWR_STATE} to Good and "
221 "chassis power is off, calling APR",
222 "START_PWR_STATE", initialPowerStatus);
Patrick Williams78c066f2024-02-13 12:25:58 -0600223 restartUnit(std::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
Andrew Geissler77a91832022-05-11 12:11:03 -0400224 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400225 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000226}
227
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400228bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000229{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600230 // Find all implementations of the UPower interface
Patrick Williams9a286db2024-01-17 06:29:47 -0600231 auto mapper = bus.new_method_call(ObjectMapper::default_service,
232 ObjectMapper::instance_path,
233 ObjectMapper::interface, "GetSubTree");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600234
Patrick Williams9a286db2024-01-17 06:29:47 -0600235 mapper.append("/", 0, std::vector<std::string>({UPowerDevice::interface}));
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600236
237 std::map<std::string, std::map<std::string, std::vector<std::string>>>
238 mapperResponse;
239
240 try
241 {
242 auto mapperResponseMsg = bus.call(mapper);
243 mapperResponseMsg.read(mapperResponse);
244 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500245 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600246 {
247 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
248 throw;
249 }
250
251 if (mapperResponse.empty())
252 {
253 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600254 }
255
256 // Iterate through all returned Upower interfaces and look for UPS's
257 for (const auto& [path, services] : mapperResponse)
258 {
259 for (const auto& serviceIter : services)
260 {
261 const std::string& service = serviceIter.first;
262
263 try
264 {
265 auto method = bus.new_method_call(service.c_str(), path.c_str(),
266 PROPERTY_INTERFACE, "GetAll");
Patrick Williams9a286db2024-01-17 06:29:47 -0600267 method.append(UPowerDevice::interface);
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600268
269 auto response = bus.call(method);
270 using Property = std::string;
271 using Value = std::variant<bool, uint>;
272 using PropertyMap = std::map<Property, Value>;
273 PropertyMap properties;
274 response.read(properties);
275
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600276 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
277 {
278 info("UPower device {OBJ_PATH} is not a UPS device",
279 "OBJ_PATH", path);
280 continue;
281 }
282
Pavithra Barithaya44bbf112024-06-21 11:43:30 -0500283 if (!std::get<bool>(properties["IsPresent"]))
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600284 {
285 // There is a UPS detected but it is not officially
286 // "present" yet. Monitor it for state change.
287 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
288 path);
289 continue;
290 }
291
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600292 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
293 {
294 info("UPS is fully charged");
295 }
296 else
297 {
298 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
299 std::get<uint>(properties["State"]));
300 server::Chassis::currentPowerStatus(
301 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400302 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600303 }
304
305 if (std::get<uint>(properties["BatteryLevel"]) ==
306 BATTERY_LVL_FULL)
307 {
308 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600309 // Only one UPS per system, we've found it and it's all
310 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400311 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600312 }
313 else
314 {
315 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
316 "UPS_BAT_LEVEL",
317 std::get<uint>(properties["BatteryLevel"]));
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 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500323 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600324 {
325 error("Error reading UPS property, error: {ERROR}, "
326 "service: {SERVICE} path: {PATH}",
327 "ERROR", e, "SERVICE", service, "PATH", path);
328 throw;
329 }
330 }
331 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400332 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600333}
334
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400335bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000336{
337 // Find all implementations of the PowerSystemInputs interface
Patrick Williams9a286db2024-01-17 06:29:47 -0600338 auto mapper = bus.new_method_call(ObjectMapper::default_service,
339 ObjectMapper::instance_path,
340 ObjectMapper::interface, "GetSubTree");
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000341
Patrick Williams9a286db2024-01-17 06:29:47 -0600342 mapper.append("/", 0,
343 std::vector<std::string>(
344 {decoratorServer::PowerSystemInputs::interface}));
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000345
346 std::map<std::string, std::map<std::string, std::vector<std::string>>>
347 mapperResponse;
348
349 try
350 {
351 auto mapperResponseMsg = bus.call(mapper);
352 mapperResponseMsg.read(mapperResponse);
353 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500354 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000355 {
356 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
357 "ERROR", e);
358 throw;
359 }
360
361 for (const auto& [path, services] : mapperResponse)
362 {
363 for (const auto& serviceIter : services)
364 {
365 const std::string& service = serviceIter.first;
366
367 try
368 {
369 auto method = bus.new_method_call(service.c_str(), path.c_str(),
370 PROPERTY_INTERFACE, "GetAll");
Patrick Williams9a286db2024-01-17 06:29:47 -0600371 method.append(decoratorServer::PowerSystemInputs::interface);
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000372
373 auto response = bus.call(method);
374 using Property = std::string;
375 using Value = std::variant<std::string>;
376 using PropertyMap = std::map<Property, Value>;
377 PropertyMap properties;
378 response.read(properties);
379
380 auto statusStr = std::get<std::string>(properties["Status"]);
381 auto status =
382 decoratorServer::PowerSystemInputs::convertStatusFromString(
383 statusStr);
384
385 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
386 {
387 info("Power System Inputs status is in Fault state");
388 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400389 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000390 }
391 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500392 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000393 {
394 error(
395 "Error reading Power System Inputs property, error: {ERROR}, "
396 "service: {SERVICE} path: {PATH}",
397 "ERROR", e, "SERVICE", service, "PATH", path);
398 throw;
399 }
400 }
401 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400402 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000403}
404
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500405void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600406{
407 debug("UPS Property Change Event Triggered");
408 std::string statusInterface;
409 std::map<std::string, std::variant<uint, bool>> msgData;
410 msg.read(statusInterface, msgData);
411
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000412 // If the change is to any of the properties we are interested in, then call
413 // determineStatusOfPower(), which looks at all the power-related
414 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600415 auto propertyMap = msgData.find("IsPresent");
416 if (propertyMap != msgData.end())
417 {
418 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
419 std::get<bool>(propertyMap->second));
420 determineStatusOfPower();
421 return;
422 }
423
424 propertyMap = msgData.find("State");
425 if (propertyMap != msgData.end())
426 {
427 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
428 std::get<uint>(propertyMap->second));
429 determineStatusOfPower();
430 return;
431 }
432
433 propertyMap = msgData.find("BatteryLevel");
434 if (propertyMap != msgData.end())
435 {
436 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
437 std::get<uint>(propertyMap->second));
438 determineStatusOfPower();
439 return;
440 }
441 return;
442}
443
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500444void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000445{
446 debug("Power System Inputs Property Change Event Triggered");
447 std::string statusInterface;
448 std::map<std::string, std::variant<std::string>> msgData;
449 msg.read(statusInterface, msgData);
450
451 // If the change is to any of the properties we are interested in, then call
452 // determineStatusOfPower(), which looks at all the power-related
453 // interfaces, to see if a power status change is needed
454 auto propertyMap = msgData.find("Status");
455 if (propertyMap != msgData.end())
456 {
457 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
458 "POWER_SYS_INPUT_STATUS",
459 std::get<std::string>(propertyMap->second));
460 determineStatusOfPower();
461 return;
462 }
463 return;
464}
465
Matthew Barthbe6efab2022-03-01 13:21:45 -0600466void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600467{
Andrew Geissler58a18012018-01-19 19:36:05 -0800468 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
469 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600470
Matthew Barthbe6efab2022-03-01 13:21:45 -0600471 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600472 method.append("replace");
473
474 this->bus.call_noreply(method);
475
476 return;
477}
478
Andrew Geissler77a91832022-05-11 12:11:03 -0400479void Chassis::restartUnit(const std::string& sysdUnit)
480{
481 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
482 SYSTEMD_INTERFACE, "RestartUnit");
483
484 method.append(sysdUnit);
485 method.append("replace");
486
487 this->bus.call_noreply(method);
488
489 return;
490}
491
Josh D. King697474c2017-03-02 11:15:55 -0600492bool Chassis::stateActive(const std::string& target)
493{
Patrick Williams2975e262020-05-13 18:01:09 -0500494 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600495 sdbusplus::message::object_path unitTargetPath;
496
Andrew Geissler58a18012018-01-19 19:36:05 -0800497 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
498 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600499
500 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600501
William A. Kennington III09568ff2018-05-11 00:03:12 -0700502 try
503 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500504 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700505 result.read(unitTargetPath);
506 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500507 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700508 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500509 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700510 return false;
511 }
Josh D. King697474c2017-03-02 11:15:55 -0600512
Andrew Geissler58a18012018-01-19 19:36:05 -0800513 method = this->bus.new_method_call(
514 SYSTEMD_SERVICE,
515 static_cast<const std::string&>(unitTargetPath).c_str(),
516 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600517
518 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600519
Anthony Wilson32c532e2018-10-25 21:56:07 -0500520 try
Josh D. King697474c2017-03-02 11:15:55 -0600521 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500522 auto result = this->bus.call(method);
523 result.read(currentState);
524 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500525 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500526 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500527 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600528 return false;
529 }
530
Patrick Williams37413dc2020-05-13 11:29:54 -0500531 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800532 return currentStateStr == ACTIVE_STATE ||
533 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600534}
535
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500536int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600537{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600538 sdbusplus::message::object_path newStateObjPath;
539 std::string newStateUnit{};
540 std::string newStateResult{};
541
Andrew Geissler58a18012018-01-19 19:36:05 -0800542 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700543 try
544 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500545 // newStateID is a throwaway that is needed in order to read the
546 // parameters that are useful out of the dbus message
547 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700548 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
549 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500550 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700551 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500552 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
553 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700554 return 0;
555 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600556
Patrick Williams78c066f2024-02-13 12:25:58 -0600557 if ((newStateUnit == std::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800558 (newStateResult == "done") &&
559 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600560 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500561 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600562 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500563 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600564 }
Potin Lai70f36d82022-03-15 10:25:39 +0800565 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800566 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800567 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800568 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500569 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800570 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500571 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600572
573 // Remove temporary file which is utilized for scenarios where the
574 // BMC is rebooted while the chassis power is still on.
575 // This file is used to indicate to chassis related systemd services
576 // that the chassis is already on and they should skip running.
577 // Once the chassis state is back to on we can clear this file.
Patrick Williams78c066f2024-02-13 12:25:58 -0600578 auto chassisFile = std::format(CHASSIS_ON_FILE, 0);
579 if (std::filesystem::exists(chassisFile))
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600580 {
Patrick Williams78c066f2024-02-13 12:25:58 -0600581 std::filesystem::remove(chassisFile);
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600582 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800583 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600584
585 return 0;
586}
587
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600588Chassis::Transition Chassis::requestedPowerTransition(Transition value)
589{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500590 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
591 "REQ_POWER_TRAN", value);
Andrew Geissler765446a2023-05-25 15:38:20 -0400592#if ONLY_ALLOW_BOOT_WHEN_BMC_READY
593 if ((value != Transition::Off) && (!utils::isBmcReady(this->bus)))
594 {
595 info("BMC State is not Ready so no chassis on operations allowed");
596 throw sdbusplus::xyz::openbmc_project::State::Chassis::Error::
597 BMCNotReady();
598 }
599#endif
Potin Lai70f36d82022-03-15 10:25:39 +0800600 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600601 return server::Chassis::requestedPowerTransition(value);
602}
603
604Chassis::PowerState Chassis::currentPowerState(PowerState value)
605{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500606 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500607 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
608 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500609
610 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530611 if (chassisPowerState == PowerState::On)
612 {
613 pohTimer.resetRemaining();
614 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500615 return chassisPowerState;
616}
617
Patrick Williams45a1ed72021-04-30 21:02:43 -0500618uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500619{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500620 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500621 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500622 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500623 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500624 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500625 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500626}
627
Patrick Williams45a1ed72021-04-30 21:02:43 -0500628void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700629{
630 if (ChassisInherit::currentPowerState() == PowerState::On)
631 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500632 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700633 }
634}
635
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500636void Chassis::restorePOHCounter()
637{
638 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800639 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500640 {
641 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500642 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500643 }
644 else
645 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500646 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500647 }
648}
649
Allen.Wangba182f02022-03-23 19:01:53 +0800650fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500651{
Patrick Williams78c066f2024-02-13 12:25:58 -0600652 fs::path path{std::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500653 std::ofstream os(path.c_str(), std::ios::binary);
654 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500655 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500656 return path;
657}
658
Allen.Wangba182f02022-03-23 19:01:53 +0800659bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500660{
Patrick Williams78c066f2024-02-13 12:25:58 -0600661 fs::path path{std::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500662 try
663 {
664 if (fs::exists(path))
665 {
666 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
667 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500668 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500669 return true;
670 }
671 return false;
672 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500673 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500674 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500675 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500676 fs::remove(path);
677 return false;
678 }
679 catch (const fs::filesystem_error& e)
680 {
681 return false;
682 }
683
684 return false;
685}
686
687void Chassis::startPOHCounter()
688{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500689 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
690 fs::create_directories(dir);
691
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500692 try
693 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700694 auto event = sdeventplus::Event::get_default();
695 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
696 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500697 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700698 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500699 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500700 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
701 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500702 phosphor::logging::commit<InternalFailure>();
703 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600704}
705
Matt Spinler9eab9862018-07-11 14:13:52 -0500706void Chassis::serializeStateChangeTime()
707{
Patrick Williams78c066f2024-02-13 12:25:58 -0600708 fs::path path{std::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500709 std::ofstream os(path.c_str(), std::ios::binary);
710 cereal::JSONOutputArchive oarchive(os);
711
712 oarchive(ChassisInherit::lastStateChangeTime(),
713 ChassisInherit::currentPowerState());
714}
715
716bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
717{
Patrick Williams78c066f2024-02-13 12:25:58 -0600718 fs::path path{std::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500719
720 try
721 {
722 if (fs::exists(path))
723 {
724 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
725 cereal::JSONInputArchive iarchive(is);
726 iarchive(time, state);
727 return true;
728 }
729 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500730 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500731 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500732 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500733 fs::remove(path);
734 }
735
736 return false;
737}
738
739void Chassis::restoreChassisStateChangeTime()
740{
741 uint64_t time;
742 PowerState state;
743
744 if (!deserializeStateChangeTime(time, state))
745 {
746 ChassisInherit::lastStateChangeTime(0);
747 }
748 else
749 {
750 ChassisInherit::lastStateChangeTime(time);
751 }
752}
753
754void Chassis::setStateChangeTime()
755{
756 using namespace std::chrono;
757 uint64_t lastTime;
758 PowerState lastState;
759
760 auto now =
761 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
762 .count();
763
764 // If power is on when the BMC is rebooted, this function will get called
765 // because sysStateChange() runs. Since the power state didn't change
766 // in this case, neither should the state change time, so check that
767 // the power state actually did change here.
768 if (deserializeStateChangeTime(lastTime, lastState))
769 {
770 if (lastState == ChassisInherit::currentPowerState())
771 {
772 return;
773 }
774 }
775
776 ChassisInherit::lastStateChangeTime(now);
777 serializeStateChangeTime();
778}
779
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500780bool Chassis::standbyVoltageRegulatorFault()
781{
782 bool regulatorFault = false;
783
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600784 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500785
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600786 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
787
788 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500789 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600790 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500791 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600792
793 if (1 == gpioval)
794 {
795 info("Detected standby voltage regulator fault");
796 regulatorFault = true;
797 }
798
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500799 return regulatorFault;
800}
801
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600802} // namespace manager
803} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700804} // namespace phosphor