blob: aa5d178cda914bdffda5d20f13d7be384ad29b9d [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>
Andrew Geissler765446a2023-05-25 15:38:20 -040019#include <xyz/openbmc_project/State/Chassis/error.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000020#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050021
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060022#include <filesystem>
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
35namespace server = sdbusplus::xyz::openbmc_project::State::server;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000036namespace decoratorServer =
37 sdbusplus::xyz::openbmc_project::State::Decorator::server;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060038
39using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070040using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050041using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050042using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080043constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
44 "obmc-chassis-poweroff@{}.target";
45constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
46 "obmc-chassis-hard-poweroff@{}.target";
47constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -050048constexpr auto CHASSIS_BLACKOUT_TGT_FMT = "obmc-chassis-blackout@{}.target";
DelphineCCChiu06881322022-11-09 08:41:25 +080049constexpr auto CHASSIS_STATE_POWERCYCLE_TGT_FMT =
50 "obmc-chassis-powercycle@{}.target";
Andrew Geissler77a91832022-05-11 12:11:03 -040051constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
52 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060053constexpr auto ACTIVE_STATE = "active";
54constexpr auto ACTIVATING_STATE = "activating";
55
Andrew Geissler2cf2a262022-02-02 14:38:41 -060056// Details at https://upower.freedesktop.org/docs/Device.html
57constexpr uint TYPE_UPS = 3;
58constexpr uint STATE_FULLY_CHARGED = 4;
59constexpr uint BATTERY_LVL_FULL = 8;
60
Andrew Geissler58a18012018-01-19 19:36:05 -080061constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
62constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060063constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
64
Josh D. King697474c2017-03-02 11:15:55 -060065constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
66constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
67
Andrew Geissler8b1f8622022-01-28 16:37:07 -060068constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
69constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
70constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
71constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000072constexpr auto POWERSYSINPUTS_INTERFACE =
73 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060074constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
75
Potin Lai70f36d82022-03-15 10:25:39 +080076void Chassis::createSystemdTargetTable()
77{
78 systemdTargetTable = {
79 // Use the hard off target to ensure we shutdown immediately
80 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
DelphineCCChiu06881322022-11-09 08:41:25 +080081 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)},
82 {Transition::PowerCycle,
83 fmt::format(CHASSIS_STATE_POWERCYCLE_TGT_FMT, id)}};
Potin Lai70f36d82022-03-15 10:25:39 +080084}
85
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060086// TODO - Will be rewritten once sdbusplus client bindings are in place
87// and persistent storage design is in place and sdbusplus
88// has read property function
89void Chassis::determineInitialState()
90{
Andrew Geissler2cf2a262022-02-02 14:38:41 -060091 // Monitor for any properties changed signals on UPower device path
92 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
93 bus,
94 sdbusplus::bus::match::rules::propertiesChangedNamespace(
95 "/org/freedesktop/UPower", UPOWER_INTERFACE),
96 [this](auto& msg) { this->uPowerChangeEvent(msg); });
97
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000098 // Monitor for any properties changed signals on PowerSystemInputs
99 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
100 bus,
101 sdbusplus::bus::match::rules::propertiesChangedNamespace(
102 fmt::format(
103 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
104 POWERSYSINPUTS_INTERFACE),
105 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
106
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600107 determineStatusOfPower();
108
Patrick Williams2975e262020-05-13 18:01:09 -0500109 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800110 auto method = this->bus.new_method_call(
111 "org.openbmc.control.Power", "/org/openbmc/control/power0",
112 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600113
114 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700115 try
116 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700117 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500118 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700119
Patrick Williams37413dc2020-05-13 11:29:54 -0500120 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700121 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500122 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700123 server::Chassis::currentPowerState(PowerState::On);
124 server::Chassis::requestedPowerTransition(Transition::On);
125 return;
126 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500127 else
128 {
129 // The system is off. If we think it should be on then
130 // we probably lost AC while up, so set a new state
131 // change time.
132 uint64_t lastTime;
133 PowerState lastState;
134
135 if (deserializeStateChangeTime(lastTime, lastState))
136 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600137 // If power was on before the BMC reboot and the reboot reason
138 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500139 if (lastState == PowerState::On)
140 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600141 info(
142 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600143
144 // Reset host sensors since system is off now
Corey_Hardesty7e0e4e92022-06-22 11:11:55 -0500145 // Ensure Power Leds are off.
146 startUnit(fmt::format(CHASSIS_BLACKOUT_TGT_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600147
Matt Spinler9eab9862018-07-11 14:13:52 -0500148 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500149 // Generate file indicating AC loss occurred
150 std::string chassisLostPowerFileFmt =
151 fmt::sprintf(CHASSIS_LOST_POWER_FILE, id);
152 fs::create_directories(BASE_FILE_DIR);
153 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
154 std::ofstream outfile(chassisPowerLossFile);
155 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600156
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000157 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600158 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000159 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600160 {
161 if (standbyVoltageRegulatorFault())
162 {
163 report<Regulator>();
164 }
165 else
166 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400167 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600168 }
169 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000170 else
171 {
172 info("Pinhole reset");
173 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500174 }
175 }
176 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700177 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500178 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700179 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700180 // It's acceptable for the pgood state service to not be available
181 // since it will notify us of the pgood state when it comes up.
182 if (e.name() != nullptr &&
183 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
184 {
185 goto fail;
186 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600187
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700188 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500189 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700190 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600191 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700192
193fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500194 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700195 server::Chassis::currentPowerState(PowerState::Off);
196 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600197
198 return;
199}
200
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600201void Chassis::determineStatusOfPower()
202{
Andrew Geissler77a91832022-05-11 12:11:03 -0400203 auto initialPowerStatus = server::Chassis::currentPowerStatus();
204
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400205 bool powerGood = determineStatusOfUPSPower();
206 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000207 {
208 return;
209 }
210
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400211 powerGood = determineStatusOfPSUPower();
212 if (powerGood)
213 {
214 // All checks passed, set power status to good
215 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400216
217 // If power status transitioned from bad to good and chassis power is
218 // off then call Auto Power Restart to see if the system should auto
219 // power on now that power status is good
220 if ((initialPowerStatus != PowerStatus::Good) &&
221 (server::Chassis::currentPowerState() == PowerState::Off))
222 {
223 info("power status transitioned from {START_PWR_STATE} to Good and "
224 "chassis power is off, calling APR",
225 "START_PWR_STATE", initialPowerStatus);
226 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
227 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400228 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000229}
230
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400231bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000232{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600233 // Find all implementations of the UPower interface
234 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
235 MAPPER_INTERFACE, "GetSubTree");
236
237 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
238
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");
269 method.append(UPOWER_INTERFACE);
270
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
340 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
341 MAPPER_INTERFACE, "GetSubTree");
342
343 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
344
345 std::map<std::string, std::map<std::string, std::vector<std::string>>>
346 mapperResponse;
347
348 try
349 {
350 auto mapperResponseMsg = bus.call(mapper);
351 mapperResponseMsg.read(mapperResponse);
352 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500353 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000354 {
355 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
356 "ERROR", e);
357 throw;
358 }
359
360 for (const auto& [path, services] : mapperResponse)
361 {
362 for (const auto& serviceIter : services)
363 {
364 const std::string& service = serviceIter.first;
365
366 try
367 {
368 auto method = bus.new_method_call(service.c_str(), path.c_str(),
369 PROPERTY_INTERFACE, "GetAll");
370 method.append(POWERSYSINPUTS_INTERFACE);
371
372 auto response = bus.call(method);
373 using Property = std::string;
374 using Value = std::variant<std::string>;
375 using PropertyMap = std::map<Property, Value>;
376 PropertyMap properties;
377 response.read(properties);
378
379 auto statusStr = std::get<std::string>(properties["Status"]);
380 auto status =
381 decoratorServer::PowerSystemInputs::convertStatusFromString(
382 statusStr);
383
384 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
385 {
386 info("Power System Inputs status is in Fault state");
387 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400388 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000389 }
390 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500391 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000392 {
393 error(
394 "Error reading Power System Inputs property, error: {ERROR}, "
395 "service: {SERVICE} path: {PATH}",
396 "ERROR", e, "SERVICE", service, "PATH", path);
397 throw;
398 }
399 }
400 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400401 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000402}
403
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500404void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600405{
406 debug("UPS Property Change Event Triggered");
407 std::string statusInterface;
408 std::map<std::string, std::variant<uint, bool>> msgData;
409 msg.read(statusInterface, msgData);
410
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000411 // If the change is to any of the properties we are interested in, then call
412 // determineStatusOfPower(), which looks at all the power-related
413 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600414 auto propertyMap = msgData.find("IsPresent");
415 if (propertyMap != msgData.end())
416 {
417 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
418 std::get<bool>(propertyMap->second));
419 determineStatusOfPower();
420 return;
421 }
422
423 propertyMap = msgData.find("State");
424 if (propertyMap != msgData.end())
425 {
426 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
427 std::get<uint>(propertyMap->second));
428 determineStatusOfPower();
429 return;
430 }
431
432 propertyMap = msgData.find("BatteryLevel");
433 if (propertyMap != msgData.end())
434 {
435 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
436 std::get<uint>(propertyMap->second));
437 determineStatusOfPower();
438 return;
439 }
440 return;
441}
442
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500443void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000444{
445 debug("Power System Inputs Property Change Event Triggered");
446 std::string statusInterface;
447 std::map<std::string, std::variant<std::string>> msgData;
448 msg.read(statusInterface, msgData);
449
450 // If the change is to any of the properties we are interested in, then call
451 // determineStatusOfPower(), which looks at all the power-related
452 // interfaces, to see if a power status change is needed
453 auto propertyMap = msgData.find("Status");
454 if (propertyMap != msgData.end())
455 {
456 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
457 "POWER_SYS_INPUT_STATUS",
458 std::get<std::string>(propertyMap->second));
459 determineStatusOfPower();
460 return;
461 }
462 return;
463}
464
Matthew Barthbe6efab2022-03-01 13:21:45 -0600465void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600466{
Andrew Geissler58a18012018-01-19 19:36:05 -0800467 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
468 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600469
Matthew Barthbe6efab2022-03-01 13:21:45 -0600470 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600471 method.append("replace");
472
473 this->bus.call_noreply(method);
474
475 return;
476}
477
Andrew Geissler77a91832022-05-11 12:11:03 -0400478void Chassis::restartUnit(const std::string& sysdUnit)
479{
480 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
481 SYSTEMD_INTERFACE, "RestartUnit");
482
483 method.append(sysdUnit);
484 method.append("replace");
485
486 this->bus.call_noreply(method);
487
488 return;
489}
490
Josh D. King697474c2017-03-02 11:15:55 -0600491bool Chassis::stateActive(const std::string& target)
492{
Patrick Williams2975e262020-05-13 18:01:09 -0500493 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600494 sdbusplus::message::object_path unitTargetPath;
495
Andrew Geissler58a18012018-01-19 19:36:05 -0800496 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
497 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600498
499 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600500
William A. Kennington III09568ff2018-05-11 00:03:12 -0700501 try
502 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500503 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700504 result.read(unitTargetPath);
505 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500506 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700507 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500508 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700509 return false;
510 }
Josh D. King697474c2017-03-02 11:15:55 -0600511
Andrew Geissler58a18012018-01-19 19:36:05 -0800512 method = this->bus.new_method_call(
513 SYSTEMD_SERVICE,
514 static_cast<const std::string&>(unitTargetPath).c_str(),
515 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600516
517 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600518
Anthony Wilson32c532e2018-10-25 21:56:07 -0500519 try
Josh D. King697474c2017-03-02 11:15:55 -0600520 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500521 auto result = this->bus.call(method);
522 result.read(currentState);
523 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500524 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500525 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500526 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600527 return false;
528 }
529
Patrick Williams37413dc2020-05-13 11:29:54 -0500530 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800531 return currentStateStr == ACTIVE_STATE ||
532 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600533}
534
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500535int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600536{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600537 sdbusplus::message::object_path newStateObjPath;
538 std::string newStateUnit{};
539 std::string newStateResult{};
540
Andrew Geissler58a18012018-01-19 19:36:05 -0800541 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700542 try
543 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500544 // newStateID is a throwaway that is needed in order to read the
545 // parameters that are useful out of the dbus message
546 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700547 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
548 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500549 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700550 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500551 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
552 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700553 return 0;
554 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600555
Allen.Wangc0895622022-03-23 15:46:47 +0800556 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800557 (newStateResult == "done") &&
558 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600559 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500560 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600561 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500562 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600563 }
Potin Lai70f36d82022-03-15 10:25:39 +0800564 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800565 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800566 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800567 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500568 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800569 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500570 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600571
572 // Remove temporary file which is utilized for scenarios where the
573 // BMC is rebooted while the chassis power is still on.
574 // This file is used to indicate to chassis related systemd services
575 // that the chassis is already on and they should skip running.
576 // Once the chassis state is back to on we can clear this file.
577 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
578 size++; // null
579 std::unique_ptr<char[]> chassisFile(new char[size]);
580 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
581 if (std::filesystem::exists(chassisFile.get()))
582 {
583 std::filesystem::remove(chassisFile.get());
584 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800585 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600586
587 return 0;
588}
589
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600590Chassis::Transition Chassis::requestedPowerTransition(Transition value)
591{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500592 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
593 "REQ_POWER_TRAN", value);
Andrew Geissler765446a2023-05-25 15:38:20 -0400594#if ONLY_ALLOW_BOOT_WHEN_BMC_READY
595 if ((value != Transition::Off) && (!utils::isBmcReady(this->bus)))
596 {
597 info("BMC State is not Ready so no chassis on operations allowed");
598 throw sdbusplus::xyz::openbmc_project::State::Chassis::Error::
599 BMCNotReady();
600 }
601#endif
Potin Lai70f36d82022-03-15 10:25:39 +0800602 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600603 return server::Chassis::requestedPowerTransition(value);
604}
605
606Chassis::PowerState Chassis::currentPowerState(PowerState value)
607{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500608 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500609 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
610 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500611
612 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530613 if (chassisPowerState == PowerState::On)
614 {
615 pohTimer.resetRemaining();
616 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500617 return chassisPowerState;
618}
619
Patrick Williams45a1ed72021-04-30 21:02:43 -0500620uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500621{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500622 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500623 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500624 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500625 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500626 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500627 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500628}
629
Patrick Williams45a1ed72021-04-30 21:02:43 -0500630void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700631{
632 if (ChassisInherit::currentPowerState() == PowerState::On)
633 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500634 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700635 }
636}
637
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500638void Chassis::restorePOHCounter()
639{
640 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800641 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500642 {
643 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500644 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500645 }
646 else
647 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500648 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500649 }
650}
651
Allen.Wangba182f02022-03-23 19:01:53 +0800652fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500653{
Allen.Wangba182f02022-03-23 19:01:53 +0800654 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500655 std::ofstream os(path.c_str(), std::ios::binary);
656 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500657 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500658 return path;
659}
660
Allen.Wangba182f02022-03-23 19:01:53 +0800661bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500662{
Allen.Wangba182f02022-03-23 19:01:53 +0800663 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500664 try
665 {
666 if (fs::exists(path))
667 {
668 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
669 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500670 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500671 return true;
672 }
673 return false;
674 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500675 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500676 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500677 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500678 fs::remove(path);
679 return false;
680 }
681 catch (const fs::filesystem_error& e)
682 {
683 return false;
684 }
685
686 return false;
687}
688
689void Chassis::startPOHCounter()
690{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500691 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
692 fs::create_directories(dir);
693
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500694 try
695 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700696 auto event = sdeventplus::Event::get_default();
697 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
698 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500699 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700700 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500701 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500702 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
703 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500704 phosphor::logging::commit<InternalFailure>();
705 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600706}
707
Matt Spinler9eab9862018-07-11 14:13:52 -0500708void Chassis::serializeStateChangeTime()
709{
Allen.Wangba182f02022-03-23 19:01:53 +0800710 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500711 std::ofstream os(path.c_str(), std::ios::binary);
712 cereal::JSONOutputArchive oarchive(os);
713
714 oarchive(ChassisInherit::lastStateChangeTime(),
715 ChassisInherit::currentPowerState());
716}
717
718bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
719{
Allen.Wangba182f02022-03-23 19:01:53 +0800720 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500721
722 try
723 {
724 if (fs::exists(path))
725 {
726 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
727 cereal::JSONInputArchive iarchive(is);
728 iarchive(time, state);
729 return true;
730 }
731 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500732 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500733 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500734 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500735 fs::remove(path);
736 }
737
738 return false;
739}
740
741void Chassis::restoreChassisStateChangeTime()
742{
743 uint64_t time;
744 PowerState state;
745
746 if (!deserializeStateChangeTime(time, state))
747 {
748 ChassisInherit::lastStateChangeTime(0);
749 }
750 else
751 {
752 ChassisInherit::lastStateChangeTime(time);
753 }
754}
755
756void Chassis::setStateChangeTime()
757{
758 using namespace std::chrono;
759 uint64_t lastTime;
760 PowerState lastState;
761
762 auto now =
763 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
764 .count();
765
766 // If power is on when the BMC is rebooted, this function will get called
767 // because sysStateChange() runs. Since the power state didn't change
768 // in this case, neither should the state change time, so check that
769 // the power state actually did change here.
770 if (deserializeStateChangeTime(lastTime, lastState))
771 {
772 if (lastState == ChassisInherit::currentPowerState())
773 {
774 return;
775 }
776 }
777
778 ChassisInherit::lastStateChangeTime(now);
779 serializeStateChangeTime();
780}
781
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500782bool Chassis::standbyVoltageRegulatorFault()
783{
784 bool regulatorFault = false;
785
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600786 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500787
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600788 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
789
790 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500791 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600792 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500793 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600794
795 if (1 == gpioval)
796 {
797 info("Detected standby voltage regulator fault");
798 regulatorFault = true;
799 }
800
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500801 return regulatorFault;
802}
803
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600804} // namespace manager
805} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700806} // namespace phosphor