blob: bfa8ff82eed23157a8062053e0bd3a45e8f465c0 [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>
Patrick Williams9a286db2024-01-17 06:29:47 -060013#include <org/freedesktop/UPower/Device/client.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050014#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050015#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060016#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070017#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070018#include <sdeventplus/event.hpp>
19#include <sdeventplus/exception.hpp>
Patrick Williams9a286db2024-01-17 06:29:47 -060020#include <xyz/openbmc_project/ObjectMapper/client.hpp>
Andrew Geissler765446a2023-05-25 15:38:20 -040021#include <xyz/openbmc_project/State/Chassis/error.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000022#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050023
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060024#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050025#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050026
Andrew Geisslera90a31a2016-12-13 16:16:28 -060027namespace phosphor
28{
29namespace state
30{
31namespace manager
32{
33
Andrew Geissler8ffdb262021-09-20 15:25:19 -050034PHOSPHOR_LOG2_USING;
35
Andrew Geisslera90a31a2016-12-13 16:16:28 -060036// When you see server:: you know we're referencing our base class
Patrick Williams7e969cb2023-08-23 16:24:23 -050037namespace server = sdbusplus::server::xyz::openbmc_project::state;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000038namespace decoratorServer =
Patrick Williams7e969cb2023-08-23 16:24:23 -050039 sdbusplus::server::xyz::openbmc_project::state::decorator;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060040
Patrick Williams9a286db2024-01-17 06:29:47 -060041using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
42using UPowerDevice = sdbusplus::client::org::freedesktop::u_power::Device<>;
43
Andrew Geisslera90a31a2016-12-13 16:16:28 -060044using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070045using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050046using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050047using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080048constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
49 "obmc-chassis-poweroff@{}.target";
50constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
51 "obmc-chassis-hard-poweroff@{}.target";
52constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -050053constexpr auto CHASSIS_BLACKOUT_TGT_FMT = "obmc-chassis-blackout@{}.target";
DelphineCCChiu06881322022-11-09 08:41:25 +080054constexpr auto CHASSIS_STATE_POWERCYCLE_TGT_FMT =
55 "obmc-chassis-powercycle@{}.target";
Andrew Geissler77a91832022-05-11 12:11:03 -040056constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
57 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060058constexpr auto ACTIVE_STATE = "active";
59constexpr auto ACTIVATING_STATE = "activating";
60
Andrew Geissler2cf2a262022-02-02 14:38:41 -060061// Details at https://upower.freedesktop.org/docs/Device.html
62constexpr uint TYPE_UPS = 3;
63constexpr uint STATE_FULLY_CHARGED = 4;
64constexpr uint BATTERY_LVL_FULL = 8;
65
Andrew Geissler58a18012018-01-19 19:36:05 -080066constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
67constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060068constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
69
Josh D. King697474c2017-03-02 11:15:55 -060070constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
71constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
72
Andrew Geissler8b1f8622022-01-28 16:37:07 -060073constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
74
Potin Lai70f36d82022-03-15 10:25:39 +080075void Chassis::createSystemdTargetTable()
76{
77 systemdTargetTable = {
78 // Use the hard off target to ensure we shutdown immediately
79 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
DelphineCCChiu06881322022-11-09 08:41:25 +080080 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)},
81 {Transition::PowerCycle,
82 fmt::format(CHASSIS_STATE_POWERCYCLE_TGT_FMT, id)}};
Potin Lai70f36d82022-03-15 10:25:39 +080083}
84
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060085// TODO - Will be rewritten once sdbusplus client bindings are in place
86// and persistent storage design is in place and sdbusplus
87// has read property function
88void Chassis::determineInitialState()
89{
Andrew Geissler2cf2a262022-02-02 14:38:41 -060090 // Monitor for any properties changed signals on UPower device path
91 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
92 bus,
93 sdbusplus::bus::match::rules::propertiesChangedNamespace(
Patrick Williams9a286db2024-01-17 06:29:47 -060094 "/org/freedesktop/UPower", UPowerDevice::interface),
Andrew Geissler2cf2a262022-02-02 14:38:41 -060095 [this](auto& msg) { this->uPowerChangeEvent(msg); });
96
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000097 // Monitor for any properties changed signals on PowerSystemInputs
98 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
99 bus,
100 sdbusplus::bus::match::rules::propertiesChangedNamespace(
101 fmt::format(
102 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
Patrick Williams9a286db2024-01-17 06:29:47 -0600103 decoratorServer::PowerSystemInputs::interface),
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000104 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
105
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600106 determineStatusOfPower();
107
Patrick Williams2975e262020-05-13 18:01:09 -0500108 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800109 auto method = this->bus.new_method_call(
110 "org.openbmc.control.Power", "/org/openbmc/control/power0",
111 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600112
113 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700114 try
115 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700116 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500117 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700118
Patrick Williams37413dc2020-05-13 11:29:54 -0500119 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700120 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500121 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700122 server::Chassis::currentPowerState(PowerState::On);
123 server::Chassis::requestedPowerTransition(Transition::On);
124 return;
125 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500126 else
127 {
128 // The system is off. If we think it should be on then
129 // we probably lost AC while up, so set a new state
130 // change time.
131 uint64_t lastTime;
132 PowerState lastState;
133
134 if (deserializeStateChangeTime(lastTime, lastState))
135 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600136 // If power was on before the BMC reboot and the reboot reason
137 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500138 if (lastState == PowerState::On)
139 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600140 info(
141 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600142
143 // Reset host sensors since system is off now
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -0500144 // Ensure Power Leds are off.
145 startUnit(fmt::format(CHASSIS_BLACKOUT_TGT_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600146
Matt Spinler9eab9862018-07-11 14:13:52 -0500147 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500148 // Generate file indicating AC loss occurred
149 std::string chassisLostPowerFileFmt =
150 fmt::sprintf(CHASSIS_LOST_POWER_FILE, id);
151 fs::create_directories(BASE_FILE_DIR);
152 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
153 std::ofstream outfile(chassisPowerLossFile);
154 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600155
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000156 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600157 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000158 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600159 {
160 if (standbyVoltageRegulatorFault())
161 {
162 report<Regulator>();
163 }
164 else
165 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400166 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600167 }
168 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000169 else
170 {
171 info("Pinhole reset");
172 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500173 }
174 }
175 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700176 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500177 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700178 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700179 // It's acceptable for the pgood state service to not be available
180 // since it will notify us of the pgood state when it comes up.
181 if (e.name() != nullptr &&
182 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
183 {
184 goto fail;
185 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600186
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700187 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500188 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700189 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600190 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700191
192fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500193 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700194 server::Chassis::currentPowerState(PowerState::Off);
195 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600196
197 return;
198}
199
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600200void Chassis::determineStatusOfPower()
201{
Andrew Geissler77a91832022-05-11 12:11:03 -0400202 auto initialPowerStatus = server::Chassis::currentPowerStatus();
203
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400204 bool powerGood = determineStatusOfUPSPower();
205 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000206 {
207 return;
208 }
209
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400210 powerGood = determineStatusOfPSUPower();
211 if (powerGood)
212 {
213 // All checks passed, set power status to good
214 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400215
216 // If power status transitioned from bad to good and chassis power is
217 // off then call Auto Power Restart to see if the system should auto
218 // power on now that power status is good
219 if ((initialPowerStatus != PowerStatus::Good) &&
220 (server::Chassis::currentPowerState() == PowerState::Off))
221 {
222 info("power status transitioned from {START_PWR_STATE} to Good and "
223 "chassis power is off, calling APR",
224 "START_PWR_STATE", initialPowerStatus);
225 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
226 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400227 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000228}
229
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400230bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000231{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600232 // Find all implementations of the UPower interface
Patrick Williams9a286db2024-01-17 06:29:47 -0600233 auto mapper = bus.new_method_call(ObjectMapper::default_service,
234 ObjectMapper::instance_path,
235 ObjectMapper::interface, "GetSubTree");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600236
Patrick Williams9a286db2024-01-17 06:29:47 -0600237 mapper.append("/", 0, std::vector<std::string>({UPowerDevice::interface}));
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600238
239 std::map<std::string, std::map<std::string, std::vector<std::string>>>
240 mapperResponse;
241
242 try
243 {
244 auto mapperResponseMsg = bus.call(mapper);
245 mapperResponseMsg.read(mapperResponse);
246 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500247 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600248 {
249 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
250 throw;
251 }
252
253 if (mapperResponse.empty())
254 {
255 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600256 }
257
258 // Iterate through all returned Upower interfaces and look for UPS's
259 for (const auto& [path, services] : mapperResponse)
260 {
261 for (const auto& serviceIter : services)
262 {
263 const std::string& service = serviceIter.first;
264
265 try
266 {
267 auto method = bus.new_method_call(service.c_str(), path.c_str(),
268 PROPERTY_INTERFACE, "GetAll");
Patrick Williams9a286db2024-01-17 06:29:47 -0600269 method.append(UPowerDevice::interface);
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600270
271 auto response = bus.call(method);
272 using Property = std::string;
273 using Value = std::variant<bool, uint>;
274 using PropertyMap = std::map<Property, Value>;
275 PropertyMap properties;
276 response.read(properties);
277
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600278 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
279 {
280 info("UPower device {OBJ_PATH} is not a UPS device",
281 "OBJ_PATH", path);
282 continue;
283 }
284
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600285 if (std::get<bool>(properties["IsPresent"]) != true)
286 {
287 // There is a UPS detected but it is not officially
288 // "present" yet. Monitor it for state change.
289 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
290 path);
291 continue;
292 }
293
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600294 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
295 {
296 info("UPS is fully charged");
297 }
298 else
299 {
300 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
301 std::get<uint>(properties["State"]));
302 server::Chassis::currentPowerStatus(
303 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400304 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600305 }
306
307 if (std::get<uint>(properties["BatteryLevel"]) ==
308 BATTERY_LVL_FULL)
309 {
310 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600311 // Only one UPS per system, we've found it and it's all
312 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400313 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600314 }
315 else
316 {
317 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
318 "UPS_BAT_LEVEL",
319 std::get<uint>(properties["BatteryLevel"]));
320 server::Chassis::currentPowerStatus(
321 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400322 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600323 }
324 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500325 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600326 {
327 error("Error reading UPS property, error: {ERROR}, "
328 "service: {SERVICE} path: {PATH}",
329 "ERROR", e, "SERVICE", service, "PATH", path);
330 throw;
331 }
332 }
333 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400334 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600335}
336
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400337bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000338{
339 // Find all implementations of the PowerSystemInputs interface
Patrick Williams9a286db2024-01-17 06:29:47 -0600340 auto mapper = bus.new_method_call(ObjectMapper::default_service,
341 ObjectMapper::instance_path,
342 ObjectMapper::interface, "GetSubTree");
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000343
Patrick Williams9a286db2024-01-17 06:29:47 -0600344 mapper.append("/", 0,
345 std::vector<std::string>(
346 {decoratorServer::PowerSystemInputs::interface}));
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000347
348 std::map<std::string, std::map<std::string, std::vector<std::string>>>
349 mapperResponse;
350
351 try
352 {
353 auto mapperResponseMsg = bus.call(mapper);
354 mapperResponseMsg.read(mapperResponse);
355 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500356 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000357 {
358 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
359 "ERROR", e);
360 throw;
361 }
362
363 for (const auto& [path, services] : mapperResponse)
364 {
365 for (const auto& serviceIter : services)
366 {
367 const std::string& service = serviceIter.first;
368
369 try
370 {
371 auto method = bus.new_method_call(service.c_str(), path.c_str(),
372 PROPERTY_INTERFACE, "GetAll");
Patrick Williams9a286db2024-01-17 06:29:47 -0600373 method.append(decoratorServer::PowerSystemInputs::interface);
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000374
375 auto response = bus.call(method);
376 using Property = std::string;
377 using Value = std::variant<std::string>;
378 using PropertyMap = std::map<Property, Value>;
379 PropertyMap properties;
380 response.read(properties);
381
382 auto statusStr = std::get<std::string>(properties["Status"]);
383 auto status =
384 decoratorServer::PowerSystemInputs::convertStatusFromString(
385 statusStr);
386
387 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
388 {
389 info("Power System Inputs status is in Fault state");
390 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400391 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000392 }
393 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500394 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000395 {
396 error(
397 "Error reading Power System Inputs property, error: {ERROR}, "
398 "service: {SERVICE} path: {PATH}",
399 "ERROR", e, "SERVICE", service, "PATH", path);
400 throw;
401 }
402 }
403 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400404 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000405}
406
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500407void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600408{
409 debug("UPS Property Change Event Triggered");
410 std::string statusInterface;
411 std::map<std::string, std::variant<uint, bool>> msgData;
412 msg.read(statusInterface, msgData);
413
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000414 // If the change is to any of the properties we are interested in, then call
415 // determineStatusOfPower(), which looks at all the power-related
416 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600417 auto propertyMap = msgData.find("IsPresent");
418 if (propertyMap != msgData.end())
419 {
420 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
421 std::get<bool>(propertyMap->second));
422 determineStatusOfPower();
423 return;
424 }
425
426 propertyMap = msgData.find("State");
427 if (propertyMap != msgData.end())
428 {
429 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
430 std::get<uint>(propertyMap->second));
431 determineStatusOfPower();
432 return;
433 }
434
435 propertyMap = msgData.find("BatteryLevel");
436 if (propertyMap != msgData.end())
437 {
438 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
439 std::get<uint>(propertyMap->second));
440 determineStatusOfPower();
441 return;
442 }
443 return;
444}
445
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500446void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000447{
448 debug("Power System Inputs Property Change Event Triggered");
449 std::string statusInterface;
450 std::map<std::string, std::variant<std::string>> msgData;
451 msg.read(statusInterface, msgData);
452
453 // If the change is to any of the properties we are interested in, then call
454 // determineStatusOfPower(), which looks at all the power-related
455 // interfaces, to see if a power status change is needed
456 auto propertyMap = msgData.find("Status");
457 if (propertyMap != msgData.end())
458 {
459 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
460 "POWER_SYS_INPUT_STATUS",
461 std::get<std::string>(propertyMap->second));
462 determineStatusOfPower();
463 return;
464 }
465 return;
466}
467
Matthew Barthbe6efab2022-03-01 13:21:45 -0600468void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600469{
Andrew Geissler58a18012018-01-19 19:36:05 -0800470 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
471 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600472
Matthew Barthbe6efab2022-03-01 13:21:45 -0600473 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600474 method.append("replace");
475
476 this->bus.call_noreply(method);
477
478 return;
479}
480
Andrew Geissler77a91832022-05-11 12:11:03 -0400481void Chassis::restartUnit(const std::string& sysdUnit)
482{
483 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
484 SYSTEMD_INTERFACE, "RestartUnit");
485
486 method.append(sysdUnit);
487 method.append("replace");
488
489 this->bus.call_noreply(method);
490
491 return;
492}
493
Josh D. King697474c2017-03-02 11:15:55 -0600494bool Chassis::stateActive(const std::string& target)
495{
Patrick Williams2975e262020-05-13 18:01:09 -0500496 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600497 sdbusplus::message::object_path unitTargetPath;
498
Andrew Geissler58a18012018-01-19 19:36:05 -0800499 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
500 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600501
502 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600503
William A. Kennington III09568ff2018-05-11 00:03:12 -0700504 try
505 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500506 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700507 result.read(unitTargetPath);
508 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500509 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700510 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500511 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700512 return false;
513 }
Josh D. King697474c2017-03-02 11:15:55 -0600514
Andrew Geissler58a18012018-01-19 19:36:05 -0800515 method = this->bus.new_method_call(
516 SYSTEMD_SERVICE,
517 static_cast<const std::string&>(unitTargetPath).c_str(),
518 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600519
520 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600521
Anthony Wilson32c532e2018-10-25 21:56:07 -0500522 try
Josh D. King697474c2017-03-02 11:15:55 -0600523 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500524 auto result = this->bus.call(method);
525 result.read(currentState);
526 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500527 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500528 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500529 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600530 return false;
531 }
532
Patrick Williams37413dc2020-05-13 11:29:54 -0500533 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800534 return currentStateStr == ACTIVE_STATE ||
535 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600536}
537
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500538int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600539{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600540 sdbusplus::message::object_path newStateObjPath;
541 std::string newStateUnit{};
542 std::string newStateResult{};
543
Andrew Geissler58a18012018-01-19 19:36:05 -0800544 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700545 try
546 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500547 // newStateID is a throwaway that is needed in order to read the
548 // parameters that are useful out of the dbus message
549 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700550 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
551 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500552 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700553 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500554 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
555 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700556 return 0;
557 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600558
Allen.Wangc0895622022-03-23 15:46:47 +0800559 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800560 (newStateResult == "done") &&
561 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600562 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500563 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600564 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500565 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600566 }
Potin Lai70f36d82022-03-15 10:25:39 +0800567 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800568 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800569 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800570 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500571 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800572 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500573 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600574
575 // Remove temporary file which is utilized for scenarios where the
576 // BMC is rebooted while the chassis power is still on.
577 // This file is used to indicate to chassis related systemd services
578 // that the chassis is already on and they should skip running.
579 // Once the chassis state is back to on we can clear this file.
580 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
581 size++; // null
582 std::unique_ptr<char[]> chassisFile(new char[size]);
583 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
584 if (std::filesystem::exists(chassisFile.get()))
585 {
586 std::filesystem::remove(chassisFile.get());
587 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800588 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600589
590 return 0;
591}
592
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600593Chassis::Transition Chassis::requestedPowerTransition(Transition value)
594{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500595 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
596 "REQ_POWER_TRAN", value);
Andrew Geissler765446a2023-05-25 15:38:20 -0400597#if ONLY_ALLOW_BOOT_WHEN_BMC_READY
598 if ((value != Transition::Off) && (!utils::isBmcReady(this->bus)))
599 {
600 info("BMC State is not Ready so no chassis on operations allowed");
601 throw sdbusplus::xyz::openbmc_project::State::Chassis::Error::
602 BMCNotReady();
603 }
604#endif
Potin Lai70f36d82022-03-15 10:25:39 +0800605 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600606 return server::Chassis::requestedPowerTransition(value);
607}
608
609Chassis::PowerState Chassis::currentPowerState(PowerState value)
610{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500611 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500612 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
613 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500614
615 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530616 if (chassisPowerState == PowerState::On)
617 {
618 pohTimer.resetRemaining();
619 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500620 return chassisPowerState;
621}
622
Patrick Williams45a1ed72021-04-30 21:02:43 -0500623uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500624{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500625 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500626 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500627 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500628 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500629 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500630 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500631}
632
Patrick Williams45a1ed72021-04-30 21:02:43 -0500633void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700634{
635 if (ChassisInherit::currentPowerState() == PowerState::On)
636 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500637 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700638 }
639}
640
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500641void Chassis::restorePOHCounter()
642{
643 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800644 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500645 {
646 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500647 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500648 }
649 else
650 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500651 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500652 }
653}
654
Allen.Wangba182f02022-03-23 19:01:53 +0800655fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500656{
Allen.Wangba182f02022-03-23 19:01:53 +0800657 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500658 std::ofstream os(path.c_str(), std::ios::binary);
659 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500660 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500661 return path;
662}
663
Allen.Wangba182f02022-03-23 19:01:53 +0800664bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500665{
Allen.Wangba182f02022-03-23 19:01:53 +0800666 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500667 try
668 {
669 if (fs::exists(path))
670 {
671 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
672 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500673 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500674 return true;
675 }
676 return false;
677 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500678 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500679 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500680 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500681 fs::remove(path);
682 return false;
683 }
684 catch (const fs::filesystem_error& e)
685 {
686 return false;
687 }
688
689 return false;
690}
691
692void Chassis::startPOHCounter()
693{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500694 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
695 fs::create_directories(dir);
696
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500697 try
698 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700699 auto event = sdeventplus::Event::get_default();
700 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
701 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500702 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700703 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500704 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500705 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
706 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500707 phosphor::logging::commit<InternalFailure>();
708 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600709}
710
Matt Spinler9eab9862018-07-11 14:13:52 -0500711void Chassis::serializeStateChangeTime()
712{
Allen.Wangba182f02022-03-23 19:01:53 +0800713 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500714 std::ofstream os(path.c_str(), std::ios::binary);
715 cereal::JSONOutputArchive oarchive(os);
716
717 oarchive(ChassisInherit::lastStateChangeTime(),
718 ChassisInherit::currentPowerState());
719}
720
721bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
722{
Allen.Wangba182f02022-03-23 19:01:53 +0800723 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500724
725 try
726 {
727 if (fs::exists(path))
728 {
729 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
730 cereal::JSONInputArchive iarchive(is);
731 iarchive(time, state);
732 return true;
733 }
734 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500735 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500736 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500737 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500738 fs::remove(path);
739 }
740
741 return false;
742}
743
744void Chassis::restoreChassisStateChangeTime()
745{
746 uint64_t time;
747 PowerState state;
748
749 if (!deserializeStateChangeTime(time, state))
750 {
751 ChassisInherit::lastStateChangeTime(0);
752 }
753 else
754 {
755 ChassisInherit::lastStateChangeTime(time);
756 }
757}
758
759void Chassis::setStateChangeTime()
760{
761 using namespace std::chrono;
762 uint64_t lastTime;
763 PowerState lastState;
764
765 auto now =
766 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
767 .count();
768
769 // If power is on when the BMC is rebooted, this function will get called
770 // because sysStateChange() runs. Since the power state didn't change
771 // in this case, neither should the state change time, so check that
772 // the power state actually did change here.
773 if (deserializeStateChangeTime(lastTime, lastState))
774 {
775 if (lastState == ChassisInherit::currentPowerState())
776 {
777 return;
778 }
779 }
780
781 ChassisInherit::lastStateChangeTime(now);
782 serializeStateChangeTime();
783}
784
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500785bool Chassis::standbyVoltageRegulatorFault()
786{
787 bool regulatorFault = false;
788
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600789 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500790
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600791 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
792
793 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500794 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600795 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500796 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600797
798 if (1 == gpioval)
799 {
800 info("Detected standby voltage regulator fault");
801 regulatorFault = true;
802 }
803
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500804 return regulatorFault;
805}
806
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600807} // namespace manager
808} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700809} // namespace phosphor