blob: 9bfdb9562f20b754b4e6b248b4678dd1ba6ee83c [file] [log] [blame]
Andrew Geisslere426b582020-05-28 12:40:55 -05001#include "config.h"
2
3#include "chassis_state_manager.hpp"
4
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -06005#include "utils.hpp"
Andrew Geisslere426b582020-05-28 12:40:55 -05006#include "xyz/openbmc_project/Common/error.hpp"
7#include "xyz/openbmc_project/State/Shutdown/Power/error.hpp"
8
Potin Lai70f36d82022-03-15 10:25:39 +08009#include <fmt/format.h>
NodeMan97bcbee4a2022-05-27 15:28:45 -050010#include <fmt/printf.h>
Potin Lai70f36d82022-03-15 10:25:39 +080011
Andrew Geisslere426b582020-05-28 12:40:55 -050012#include <cereal/archives/json.hpp>
13#include <phosphor-logging/elog-errors.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050014#include <phosphor-logging/lg2.hpp>
Andrew Geissler0029a5d2017-01-24 14:48:35 -060015#include <sdbusplus/bus.hpp>
William A. Kennington III09568ff2018-05-11 00:03:12 -070016#include <sdbusplus/exception.hpp>
William A. Kennington IIId998f822018-10-17 23:17:57 -070017#include <sdeventplus/event.hpp>
18#include <sdeventplus/exception.hpp>
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000019#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050020
Andrew Geisslerf2b22e82021-03-12 14:47:03 -060021#include <filesystem>
Andrew Geisslere426b582020-05-28 12:40:55 -050022#include <fstream>
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -050023
Andrew Geisslera90a31a2016-12-13 16:16:28 -060024namespace phosphor
25{
26namespace state
27{
28namespace manager
29{
30
Andrew Geissler8ffdb262021-09-20 15:25:19 -050031PHOSPHOR_LOG2_USING;
32
Andrew Geisslera90a31a2016-12-13 16:16:28 -060033// When you see server:: you know we're referencing our base class
34namespace server = sdbusplus::xyz::openbmc_project::State::server;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000035namespace decoratorServer =
36 sdbusplus::xyz::openbmc_project::State::Decorator::server;
Andrew Geisslera90a31a2016-12-13 16:16:28 -060037
38using namespace phosphor::logging;
William A. Kennington IIId998f822018-10-17 23:17:57 -070039using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Aatir Manzur27115ae2019-07-23 16:25:38 -050040using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Blackout;
Ben Tyner2c36e5a2021-07-12 14:56:49 -050041using sdbusplus::xyz::openbmc_project::State::Shutdown::Power::Error::Regulator;
Potin Lai70f36d82022-03-15 10:25:39 +080042constexpr auto CHASSIS_STATE_POWEROFF_TGT_FMT =
43 "obmc-chassis-poweroff@{}.target";
44constexpr auto CHASSIS_STATE_HARD_POWEROFF_TGT_FMT =
45 "obmc-chassis-hard-poweroff@{}.target";
46constexpr auto CHASSIS_STATE_POWERON_TGT_FMT = "obmc-chassis-poweron@{}.target";
47constexpr auto RESET_HOST_SENSORS_SVC_FMT =
48 "phosphor-reset-sensor-states@{}.service";
Andrew Geissler77a91832022-05-11 12:11:03 -040049constexpr auto AUTO_POWER_RESTORE_SVC_FMT =
50 "phosphor-discover-system-state@{}.service";
Josh D. King697474c2017-03-02 11:15:55 -060051constexpr auto ACTIVE_STATE = "active";
52constexpr auto ACTIVATING_STATE = "activating";
53
Andrew Geissler2cf2a262022-02-02 14:38:41 -060054// Details at https://upower.freedesktop.org/docs/Device.html
55constexpr uint TYPE_UPS = 3;
56constexpr uint STATE_FULLY_CHARGED = 4;
57constexpr uint BATTERY_LVL_FULL = 8;
58
Andrew Geissler58a18012018-01-19 19:36:05 -080059constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
60constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
Andrew Geisslerce80f242017-01-24 13:25:33 -060061constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
62
Josh D. King697474c2017-03-02 11:15:55 -060063constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
64constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
65
Andrew Geissler8b1f8622022-01-28 16:37:07 -060066constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
67constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
68constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
69constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
Adriana Kobylak396ed8a2022-03-22 15:45:41 +000070constexpr auto POWERSYSINPUTS_INTERFACE =
71 "xyz.openbmc_project.State.Decorator.PowerSystemInputs";
Andrew Geissler8b1f8622022-01-28 16:37:07 -060072constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
73
Andrew Geissler0029a5d2017-01-24 14:48:35 -060074void Chassis::subscribeToSystemdSignals()
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060075{
Andrew Geissler3a30b052019-05-14 15:54:37 -050076 try
77 {
78 auto method = this->bus.new_method_call(
79 SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, SYSTEMD_INTERFACE, "Subscribe");
Andrew Geissler5b950272019-05-24 12:27:51 -050080 this->bus.call(method);
Andrew Geissler3a30b052019-05-14 15:54:37 -050081 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -050082 catch (const sdbusplus::exception_t& e)
Andrew Geissler3a30b052019-05-14 15:54:37 -050083 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -050084 error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
Andrew Geissler3a30b052019-05-14 15:54:37 -050085 elog<InternalFailure>();
86 }
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060087
Andrew Geissler0029a5d2017-01-24 14:48:35 -060088 return;
Andrew Geissler2ec3a7e2016-12-13 22:01:28 -060089}
90
Potin Lai70f36d82022-03-15 10:25:39 +080091void Chassis::createSystemdTargetTable()
92{
93 systemdTargetTable = {
94 // Use the hard off target to ensure we shutdown immediately
95 {Transition::Off, fmt::format(CHASSIS_STATE_HARD_POWEROFF_TGT_FMT, id)},
96 {Transition::On, fmt::format(CHASSIS_STATE_POWERON_TGT_FMT, id)}};
97}
98
Andrew Geisslerdff50ed2016-12-13 20:39:04 -060099// TODO - Will be rewritten once sdbusplus client bindings are in place
100// and persistent storage design is in place and sdbusplus
101// has read property function
102void Chassis::determineInitialState()
103{
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600104 // Monitor for any properties changed signals on UPower device path
105 uPowerPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
106 bus,
107 sdbusplus::bus::match::rules::propertiesChangedNamespace(
108 "/org/freedesktop/UPower", UPOWER_INTERFACE),
109 [this](auto& msg) { this->uPowerChangeEvent(msg); });
110
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000111 // Monitor for any properties changed signals on PowerSystemInputs
112 powerSysInputsPropChangeSignal = std::make_unique<sdbusplus::bus::match_t>(
113 bus,
114 sdbusplus::bus::match::rules::propertiesChangedNamespace(
115 fmt::format(
116 "/xyz/openbmc_project/power/power_supplies/chassis{}/psus", id),
117 POWERSYSINPUTS_INTERFACE),
118 [this](auto& msg) { this->powerSysInputsChangeEvent(msg); });
119
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600120 determineStatusOfPower();
121
Patrick Williams2975e262020-05-13 18:01:09 -0500122 std::variant<int> pgood = -1;
Andrew Geissler58a18012018-01-19 19:36:05 -0800123 auto method = this->bus.new_method_call(
124 "org.openbmc.control.Power", "/org/openbmc/control/power0",
125 "org.freedesktop.DBus.Properties", "Get");
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600126
127 method.append("org.openbmc.control.Power", "pgood");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700128 try
129 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700130 auto reply = this->bus.call(method);
Anthony Wilson32c532e2018-10-25 21:56:07 -0500131 reply.read(pgood);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700132
Patrick Williams37413dc2020-05-13 11:29:54 -0500133 if (std::get<int>(pgood) == 1)
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700134 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500135 info("Initial Chassis State will be On");
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700136 server::Chassis::currentPowerState(PowerState::On);
137 server::Chassis::requestedPowerTransition(Transition::On);
138 return;
139 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500140 else
141 {
142 // The system is off. If we think it should be on then
143 // we probably lost AC while up, so set a new state
144 // change time.
145 uint64_t lastTime;
146 PowerState lastState;
147
148 if (deserializeStateChangeTime(lastTime, lastState))
149 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600150 // If power was on before the BMC reboot and the reboot reason
151 // was not a pinhole reset, log an error
Matt Spinler9eab9862018-07-11 14:13:52 -0500152 if (lastState == PowerState::On)
153 {
Andrew Geissler7ed36232022-01-26 13:49:28 -0600154 info(
155 "Chassis power was on before the BMC reboot and it is off now");
Matthew Barthbe6efab2022-03-01 13:21:45 -0600156
157 // Reset host sensors since system is off now
NodeMan97bcbee4a2022-05-27 15:28:45 -0500158 // TODO: when CHASSIS_BLACKOUT_TGT will include this service
159 // Needs to be removed when the blackout target is merged
Potin Lai70f36d82022-03-15 10:25:39 +0800160 startUnit(fmt::format(RESET_HOST_SENSORS_SVC_FMT, id));
Matthew Barthbe6efab2022-03-01 13:21:45 -0600161
Matt Spinler9eab9862018-07-11 14:13:52 -0500162 setStateChangeTime();
NodeMan97bcbee4a2022-05-27 15:28:45 -0500163 // Generate file indicating AC loss occurred
164 std::string chassisLostPowerFileFmt =
165 fmt::sprintf(CHASSIS_LOST_POWER_FILE, id);
166 fs::create_directories(BASE_FILE_DIR);
167 fs::path chassisPowerLossFile{chassisLostPowerFileFmt};
168 std::ofstream outfile(chassisPowerLossFile);
169 outfile.close();
Andrew Geissler7ed36232022-01-26 13:49:28 -0600170
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000171 // 0 indicates pinhole reset. 1 is NOT pinhole reset
Andrew Geissler7ed36232022-01-26 13:49:28 -0600172 if (phosphor::state::manager::utils::getGpioValue(
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000173 "reset-cause-pinhole") != 0)
Andrew Geissler7ed36232022-01-26 13:49:28 -0600174 {
175 if (standbyVoltageRegulatorFault())
176 {
177 report<Regulator>();
178 }
179 else
180 {
Mike Capps073fa2b2022-03-15 16:06:45 -0400181 report<Blackout>(Entry::Level::Critical);
Andrew Geissler7ed36232022-01-26 13:49:28 -0600182 }
183 }
Brandon Wyman4fe860f2022-03-16 00:56:53 +0000184 else
185 {
186 info("Pinhole reset");
187 }
Matt Spinler9eab9862018-07-11 14:13:52 -0500188 }
189 }
190 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700191 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500192 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700193 {
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700194 // It's acceptable for the pgood state service to not be available
195 // since it will notify us of the pgood state when it comes up.
196 if (e.name() != nullptr &&
197 strcmp("org.freedesktop.DBus.Error.ServiceUnknown", e.name()) == 0)
198 {
199 goto fail;
200 }
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600201
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700202 // Only log for unexpected error types.
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500203 error("Error performing call to get pgood: {ERROR}", "ERROR", e);
William A. Kennington III9a2f37c2018-06-28 16:18:37 -0700204 goto fail;
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600205 }
William A. Kennington III09568ff2018-05-11 00:03:12 -0700206
207fail:
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500208 info("Initial Chassis State will be Off");
William A. Kennington III09568ff2018-05-11 00:03:12 -0700209 server::Chassis::currentPowerState(PowerState::Off);
210 server::Chassis::requestedPowerTransition(Transition::Off);
Andrew Geisslerdff50ed2016-12-13 20:39:04 -0600211
212 return;
213}
214
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600215void Chassis::determineStatusOfPower()
216{
Andrew Geissler77a91832022-05-11 12:11:03 -0400217 auto initialPowerStatus = server::Chassis::currentPowerStatus();
218
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400219 bool powerGood = determineStatusOfUPSPower();
220 if (!powerGood)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000221 {
222 return;
223 }
224
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400225 powerGood = determineStatusOfPSUPower();
226 if (powerGood)
227 {
228 // All checks passed, set power status to good
229 server::Chassis::currentPowerStatus(PowerStatus::Good);
Andrew Geissler77a91832022-05-11 12:11:03 -0400230
231 // If power status transitioned from bad to good and chassis power is
232 // off then call Auto Power Restart to see if the system should auto
233 // power on now that power status is good
234 if ((initialPowerStatus != PowerStatus::Good) &&
235 (server::Chassis::currentPowerState() == PowerState::Off))
236 {
237 info("power status transitioned from {START_PWR_STATE} to Good and "
238 "chassis power is off, calling APR",
239 "START_PWR_STATE", initialPowerStatus);
240 restartUnit(fmt::format(AUTO_POWER_RESTORE_SVC_FMT, this->id));
241 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400242 }
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000243}
244
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400245bool Chassis::determineStatusOfUPSPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000246{
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600247 // Find all implementations of the UPower interface
248 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
249 MAPPER_INTERFACE, "GetSubTree");
250
251 mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
252
253 std::map<std::string, std::map<std::string, std::vector<std::string>>>
254 mapperResponse;
255
256 try
257 {
258 auto mapperResponseMsg = bus.call(mapper);
259 mapperResponseMsg.read(mapperResponse);
260 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500261 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600262 {
263 error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
264 throw;
265 }
266
267 if (mapperResponse.empty())
268 {
269 debug("No UPower devices found in system");
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600270 }
271
272 // Iterate through all returned Upower interfaces and look for UPS's
273 for (const auto& [path, services] : mapperResponse)
274 {
275 for (const auto& serviceIter : services)
276 {
277 const std::string& service = serviceIter.first;
278
279 try
280 {
281 auto method = bus.new_method_call(service.c_str(), path.c_str(),
282 PROPERTY_INTERFACE, "GetAll");
283 method.append(UPOWER_INTERFACE);
284
285 auto response = bus.call(method);
286 using Property = std::string;
287 using Value = std::variant<bool, uint>;
288 using PropertyMap = std::map<Property, Value>;
289 PropertyMap properties;
290 response.read(properties);
291
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600292 if (std::get<uint>(properties["Type"]) != TYPE_UPS)
293 {
294 info("UPower device {OBJ_PATH} is not a UPS device",
295 "OBJ_PATH", path);
296 continue;
297 }
298
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600299 if (std::get<bool>(properties["IsPresent"]) != true)
300 {
301 // There is a UPS detected but it is not officially
302 // "present" yet. Monitor it for state change.
303 info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
304 path);
305 continue;
306 }
307
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600308 if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
309 {
310 info("UPS is fully charged");
311 }
312 else
313 {
314 info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
315 std::get<uint>(properties["State"]));
316 server::Chassis::currentPowerStatus(
317 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400318 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600319 }
320
321 if (std::get<uint>(properties["BatteryLevel"]) ==
322 BATTERY_LVL_FULL)
323 {
324 info("UPS Battery Level is Full");
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600325 // Only one UPS per system, we've found it and it's all
326 // good so exit function
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400327 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600328 }
329 else
330 {
331 info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
332 "UPS_BAT_LEVEL",
333 std::get<uint>(properties["BatteryLevel"]));
334 server::Chassis::currentPowerStatus(
335 PowerStatus::UninterruptiblePowerSupply);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400336 return false;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600337 }
338 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500339 catch (const sdbusplus::exception_t& e)
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600340 {
341 error("Error reading UPS property, error: {ERROR}, "
342 "service: {SERVICE} path: {PATH}",
343 "ERROR", e, "SERVICE", service, "PATH", path);
344 throw;
345 }
346 }
347 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400348 return true;
Andrew Geissler8b1f8622022-01-28 16:37:07 -0600349}
350
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400351bool Chassis::determineStatusOfPSUPower()
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000352{
353 // Find all implementations of the PowerSystemInputs interface
354 auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
355 MAPPER_INTERFACE, "GetSubTree");
356
357 mapper.append("/", 0, std::vector<std::string>({POWERSYSINPUTS_INTERFACE}));
358
359 std::map<std::string, std::map<std::string, std::vector<std::string>>>
360 mapperResponse;
361
362 try
363 {
364 auto mapperResponseMsg = bus.call(mapper);
365 mapperResponseMsg.read(mapperResponse);
366 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500367 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000368 {
369 error("Error in mapper GetSubTree call for PowerSystemInputs: {ERROR}",
370 "ERROR", e);
371 throw;
372 }
373
374 for (const auto& [path, services] : mapperResponse)
375 {
376 for (const auto& serviceIter : services)
377 {
378 const std::string& service = serviceIter.first;
379
380 try
381 {
382 auto method = bus.new_method_call(service.c_str(), path.c_str(),
383 PROPERTY_INTERFACE, "GetAll");
384 method.append(POWERSYSINPUTS_INTERFACE);
385
386 auto response = bus.call(method);
387 using Property = std::string;
388 using Value = std::variant<std::string>;
389 using PropertyMap = std::map<Property, Value>;
390 PropertyMap properties;
391 response.read(properties);
392
393 auto statusStr = std::get<std::string>(properties["Status"]);
394 auto status =
395 decoratorServer::PowerSystemInputs::convertStatusFromString(
396 statusStr);
397
398 if (status == decoratorServer::PowerSystemInputs::Status::Fault)
399 {
400 info("Power System Inputs status is in Fault state");
401 server::Chassis::currentPowerStatus(PowerStatus::BrownOut);
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400402 return false;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000403 }
404 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500405 catch (const sdbusplus::exception_t& e)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000406 {
407 error(
408 "Error reading Power System Inputs property, error: {ERROR}, "
409 "service: {SERVICE} path: {PATH}",
410 "ERROR", e, "SERVICE", service, "PATH", path);
411 throw;
412 }
413 }
414 }
Andrew Geissler66aacdc2022-05-11 08:31:47 -0400415 return true;
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000416}
417
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500418void Chassis::uPowerChangeEvent(sdbusplus::message_t& msg)
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600419{
420 debug("UPS Property Change Event Triggered");
421 std::string statusInterface;
422 std::map<std::string, std::variant<uint, bool>> msgData;
423 msg.read(statusInterface, msgData);
424
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000425 // If the change is to any of the properties we are interested in, then call
426 // determineStatusOfPower(), which looks at all the power-related
427 // interfaces, to see if a power status change is needed
Andrew Geissler2cf2a262022-02-02 14:38:41 -0600428 auto propertyMap = msgData.find("IsPresent");
429 if (propertyMap != msgData.end())
430 {
431 info("UPS presence changed to {UPS_PRES_INFO}", "UPS_PRES_INFO",
432 std::get<bool>(propertyMap->second));
433 determineStatusOfPower();
434 return;
435 }
436
437 propertyMap = msgData.find("State");
438 if (propertyMap != msgData.end())
439 {
440 info("UPS State changed to {UPS_STATE}", "UPS_STATE",
441 std::get<uint>(propertyMap->second));
442 determineStatusOfPower();
443 return;
444 }
445
446 propertyMap = msgData.find("BatteryLevel");
447 if (propertyMap != msgData.end())
448 {
449 info("UPS BatteryLevel changed to {UPS_BAT_LEVEL}", "UPS_BAT_LEVEL",
450 std::get<uint>(propertyMap->second));
451 determineStatusOfPower();
452 return;
453 }
454 return;
455}
456
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500457void Chassis::powerSysInputsChangeEvent(sdbusplus::message_t& msg)
Adriana Kobylak396ed8a2022-03-22 15:45:41 +0000458{
459 debug("Power System Inputs Property Change Event Triggered");
460 std::string statusInterface;
461 std::map<std::string, std::variant<std::string>> msgData;
462 msg.read(statusInterface, msgData);
463
464 // If the change is to any of the properties we are interested in, then call
465 // determineStatusOfPower(), which looks at all the power-related
466 // interfaces, to see if a power status change is needed
467 auto propertyMap = msgData.find("Status");
468 if (propertyMap != msgData.end())
469 {
470 info("Power System Inputs status changed to {POWER_SYS_INPUT_STATUS}",
471 "POWER_SYS_INPUT_STATUS",
472 std::get<std::string>(propertyMap->second));
473 determineStatusOfPower();
474 return;
475 }
476 return;
477}
478
Matthew Barthbe6efab2022-03-01 13:21:45 -0600479void Chassis::startUnit(const std::string& sysdUnit)
Andrew Geisslerce80f242017-01-24 13:25:33 -0600480{
Andrew Geissler58a18012018-01-19 19:36:05 -0800481 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
482 SYSTEMD_INTERFACE, "StartUnit");
Andrew Geisslerce80f242017-01-24 13:25:33 -0600483
Matthew Barthbe6efab2022-03-01 13:21:45 -0600484 method.append(sysdUnit);
Andrew Geisslerce80f242017-01-24 13:25:33 -0600485 method.append("replace");
486
487 this->bus.call_noreply(method);
488
489 return;
490}
491
Andrew Geissler77a91832022-05-11 12:11:03 -0400492void Chassis::restartUnit(const std::string& sysdUnit)
493{
494 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
495 SYSTEMD_INTERFACE, "RestartUnit");
496
497 method.append(sysdUnit);
498 method.append("replace");
499
500 this->bus.call_noreply(method);
501
502 return;
503}
504
Josh D. King697474c2017-03-02 11:15:55 -0600505bool Chassis::stateActive(const std::string& target)
506{
Patrick Williams2975e262020-05-13 18:01:09 -0500507 std::variant<std::string> currentState;
Josh D. King697474c2017-03-02 11:15:55 -0600508 sdbusplus::message::object_path unitTargetPath;
509
Andrew Geissler58a18012018-01-19 19:36:05 -0800510 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
511 SYSTEMD_INTERFACE, "GetUnit");
Josh D. King697474c2017-03-02 11:15:55 -0600512
513 method.append(target);
Josh D. King697474c2017-03-02 11:15:55 -0600514
William A. Kennington III09568ff2018-05-11 00:03:12 -0700515 try
516 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500517 auto result = this->bus.call(method);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700518 result.read(unitTargetPath);
519 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500520 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700521 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500522 error("Error in GetUnit call: {ERROR}", "ERROR", e);
William A. Kennington III09568ff2018-05-11 00:03:12 -0700523 return false;
524 }
Josh D. King697474c2017-03-02 11:15:55 -0600525
Andrew Geissler58a18012018-01-19 19:36:05 -0800526 method = this->bus.new_method_call(
527 SYSTEMD_SERVICE,
528 static_cast<const std::string&>(unitTargetPath).c_str(),
529 SYSTEMD_PROPERTY_IFACE, "Get");
Josh D. King697474c2017-03-02 11:15:55 -0600530
531 method.append(SYSTEMD_INTERFACE_UNIT, "ActiveState");
Josh D. King697474c2017-03-02 11:15:55 -0600532
Anthony Wilson32c532e2018-10-25 21:56:07 -0500533 try
Josh D. King697474c2017-03-02 11:15:55 -0600534 {
Anthony Wilson32c532e2018-10-25 21:56:07 -0500535 auto result = this->bus.call(method);
536 result.read(currentState);
537 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500538 catch (const sdbusplus::exception_t& e)
Anthony Wilson32c532e2018-10-25 21:56:07 -0500539 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500540 error("Error in ActiveState Get: {ERROR}", "ERROR", e);
Josh D. King697474c2017-03-02 11:15:55 -0600541 return false;
542 }
543
Patrick Williams37413dc2020-05-13 11:29:54 -0500544 const auto& currentStateStr = std::get<std::string>(currentState);
William A. Kennington III7a0689a2018-11-12 17:19:33 -0800545 return currentStateStr == ACTIVE_STATE ||
546 currentStateStr == ACTIVATING_STATE;
Josh D. King697474c2017-03-02 11:15:55 -0600547}
548
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500549int Chassis::sysStateChange(sdbusplus::message_t& msg)
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600550{
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600551 sdbusplus::message::object_path newStateObjPath;
552 std::string newStateUnit{};
553 std::string newStateResult{};
554
Andrew Geissler58a18012018-01-19 19:36:05 -0800555 // Read the msg and populate each variable
William A. Kennington III09568ff2018-05-11 00:03:12 -0700556 try
557 {
Andrew Geissler9b8af4f2019-09-12 14:19:14 -0500558 // newStateID is a throwaway that is needed in order to read the
559 // parameters that are useful out of the dbus message
560 uint32_t newStateID{};
William A. Kennington III09568ff2018-05-11 00:03:12 -0700561 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
562 }
Patrick Williamsf053e6f2022-07-22 19:26:54 -0500563 catch (const sdbusplus::exception_t& e)
William A. Kennington III09568ff2018-05-11 00:03:12 -0700564 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500565 error("Error in state change - bad encoding: {ERROR} {REPLY_SIG}",
566 "ERROR", e, "REPLY_SIG", msg.get_signature());
William A. Kennington III09568ff2018-05-11 00:03:12 -0700567 return 0;
568 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600569
Allen.Wangc0895622022-03-23 15:46:47 +0800570 if ((newStateUnit == fmt::format(CHASSIS_STATE_POWEROFF_TGT_FMT, id)) &&
Potin Lai70f36d82022-03-15 10:25:39 +0800571 (newStateResult == "done") &&
572 (!stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600573 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500574 info("Received signal that power OFF is complete");
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600575 this->currentPowerState(server::Chassis::PowerState::Off);
Matt Spinler9eab9862018-07-11 14:13:52 -0500576 this->setStateChangeTime();
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600577 }
Potin Lai70f36d82022-03-15 10:25:39 +0800578 else if ((newStateUnit == systemdTargetTable[Transition::On]) &&
Andrew Geissler58a18012018-01-19 19:36:05 -0800579 (newStateResult == "done") &&
Potin Lai70f36d82022-03-15 10:25:39 +0800580 (stateActive(systemdTargetTable[Transition::On])))
Andrew Geissler58a18012018-01-19 19:36:05 -0800581 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500582 info("Received signal that power ON is complete");
Andrew Geissler58a18012018-01-19 19:36:05 -0800583 this->currentPowerState(server::Chassis::PowerState::On);
Matt Spinler9eab9862018-07-11 14:13:52 -0500584 this->setStateChangeTime();
Andrew Geisslerf2b22e82021-03-12 14:47:03 -0600585
586 // Remove temporary file which is utilized for scenarios where the
587 // BMC is rebooted while the chassis power is still on.
588 // This file is used to indicate to chassis related systemd services
589 // that the chassis is already on and they should skip running.
590 // Once the chassis state is back to on we can clear this file.
591 auto size = std::snprintf(nullptr, 0, CHASSIS_ON_FILE, 0);
592 size++; // null
593 std::unique_ptr<char[]> chassisFile(new char[size]);
594 std::snprintf(chassisFile.get(), size, CHASSIS_ON_FILE, 0);
595 if (std::filesystem::exists(chassisFile.get()))
596 {
597 std::filesystem::remove(chassisFile.get());
598 }
Andrew Geissler58a18012018-01-19 19:36:05 -0800599 }
Andrew Geissler0029a5d2017-01-24 14:48:35 -0600600
601 return 0;
602}
603
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600604Chassis::Transition Chassis::requestedPowerTransition(Transition value)
605{
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500606 info("Change to Chassis Requested Power State: {REQ_POWER_TRAN}",
607 "REQ_POWER_TRAN", value);
Potin Lai70f36d82022-03-15 10:25:39 +0800608 startUnit(systemdTargetTable.find(value)->second);
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600609 return server::Chassis::requestedPowerTransition(value);
610}
611
612Chassis::PowerState Chassis::currentPowerState(PowerState value)
613{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500614 PowerState chassisPowerState;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500615 info("Change to Chassis Power State: {CUR_POWER_STATE}", "CUR_POWER_STATE",
616 value);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500617
618 chassisPowerState = server::Chassis::currentPowerState(value);
shamim ali6d582a82022-03-15 18:19:32 +0530619 if (chassisPowerState == PowerState::On)
620 {
621 pohTimer.resetRemaining();
622 }
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500623 return chassisPowerState;
624}
625
Patrick Williams45a1ed72021-04-30 21:02:43 -0500626uint32_t Chassis::pohCounter(uint32_t value)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500627{
Patrick Williams45a1ed72021-04-30 21:02:43 -0500628 if (value != pohCounter())
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500629 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500630 ChassisInherit::pohCounter(value);
Matt Spinler81957842018-07-11 10:37:12 -0500631 serializePOH();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500632 }
Patrick Williams45a1ed72021-04-30 21:02:43 -0500633 return pohCounter();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500634}
635
Patrick Williams45a1ed72021-04-30 21:02:43 -0500636void Chassis::pohCallback()
William A. Kennington IIId998f822018-10-17 23:17:57 -0700637{
638 if (ChassisInherit::currentPowerState() == PowerState::On)
639 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500640 pohCounter(pohCounter() + 1);
William A. Kennington IIId998f822018-10-17 23:17:57 -0700641 }
642}
643
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500644void Chassis::restorePOHCounter()
645{
646 uint32_t counter;
Allen.Wangba182f02022-03-23 19:01:53 +0800647 if (!deserializePOH(counter))
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500648 {
649 // set to default value
Patrick Williams45a1ed72021-04-30 21:02:43 -0500650 pohCounter(0);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500651 }
652 else
653 {
Patrick Williams45a1ed72021-04-30 21:02:43 -0500654 pohCounter(counter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500655 }
656}
657
Allen.Wangba182f02022-03-23 19:01:53 +0800658fs::path Chassis::serializePOH()
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500659{
Allen.Wangba182f02022-03-23 19:01:53 +0800660 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500661 std::ofstream os(path.c_str(), std::ios::binary);
662 cereal::JSONOutputArchive oarchive(os);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500663 oarchive(pohCounter());
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500664 return path;
665}
666
Allen.Wangba182f02022-03-23 19:01:53 +0800667bool Chassis::deserializePOH(uint32_t& pohCounter)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500668{
Allen.Wangba182f02022-03-23 19:01:53 +0800669 fs::path path{fmt::format(POH_COUNTER_PERSIST_PATH, id)};
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500670 try
671 {
672 if (fs::exists(path))
673 {
674 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
675 cereal::JSONInputArchive iarchive(is);
Patrick Williams45a1ed72021-04-30 21:02:43 -0500676 iarchive(pohCounter);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500677 return true;
678 }
679 return false;
680 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500681 catch (const cereal::Exception& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500682 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500683 error("deserialize exception: {ERROR}", "ERROR", e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500684 fs::remove(path);
685 return false;
686 }
687 catch (const fs::filesystem_error& e)
688 {
689 return false;
690 }
691
692 return false;
693}
694
695void Chassis::startPOHCounter()
696{
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500697 auto dir = fs::path(POH_COUNTER_PERSIST_PATH).parent_path();
698 fs::create_directories(dir);
699
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500700 try
701 {
William A. Kennington IIId998f822018-10-17 23:17:57 -0700702 auto event = sdeventplus::Event::get_default();
703 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
704 event.loop();
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500705 }
William A. Kennington IIId998f822018-10-17 23:17:57 -0700706 catch (const sdeventplus::SdEventError& e)
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500707 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500708 error("Error occurred during the sdeventplus loop: {ERROR}", "ERROR",
709 e);
Nagaraju Goruganticb781fe2018-04-06 13:41:01 -0500710 phosphor::logging::commit<InternalFailure>();
711 }
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600712}
713
Matt Spinler9eab9862018-07-11 14:13:52 -0500714void Chassis::serializeStateChangeTime()
715{
Allen.Wangba182f02022-03-23 19:01:53 +0800716 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500717 std::ofstream os(path.c_str(), std::ios::binary);
718 cereal::JSONOutputArchive oarchive(os);
719
720 oarchive(ChassisInherit::lastStateChangeTime(),
721 ChassisInherit::currentPowerState());
722}
723
724bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state)
725{
Allen.Wangba182f02022-03-23 19:01:53 +0800726 fs::path path{fmt::format(CHASSIS_STATE_CHANGE_PERSIST_PATH, id)};
Matt Spinler9eab9862018-07-11 14:13:52 -0500727
728 try
729 {
730 if (fs::exists(path))
731 {
732 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
733 cereal::JSONInputArchive iarchive(is);
734 iarchive(time, state);
735 return true;
736 }
737 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500738 catch (const std::exception& e)
Matt Spinler9eab9862018-07-11 14:13:52 -0500739 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500740 error("deserialize exception: {ERROR}", "ERROR", e);
Matt Spinler9eab9862018-07-11 14:13:52 -0500741 fs::remove(path);
742 }
743
744 return false;
745}
746
747void Chassis::restoreChassisStateChangeTime()
748{
749 uint64_t time;
750 PowerState state;
751
752 if (!deserializeStateChangeTime(time, state))
753 {
754 ChassisInherit::lastStateChangeTime(0);
755 }
756 else
757 {
758 ChassisInherit::lastStateChangeTime(time);
759 }
760}
761
762void Chassis::setStateChangeTime()
763{
764 using namespace std::chrono;
765 uint64_t lastTime;
766 PowerState lastState;
767
768 auto now =
769 duration_cast<milliseconds>(system_clock::now().time_since_epoch())
770 .count();
771
772 // If power is on when the BMC is rebooted, this function will get called
773 // because sysStateChange() runs. Since the power state didn't change
774 // in this case, neither should the state change time, so check that
775 // the power state actually did change here.
776 if (deserializeStateChangeTime(lastTime, lastState))
777 {
778 if (lastState == ChassisInherit::currentPowerState())
779 {
780 return;
781 }
782 }
783
784 ChassisInherit::lastStateChangeTime(now);
785 serializeStateChangeTime();
786}
787
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500788bool Chassis::standbyVoltageRegulatorFault()
789{
790 bool regulatorFault = false;
791
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600792 // find standby voltage regulator fault via gpiog
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500793
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600794 auto gpioval = utils::getGpioValue("regulator-standby-faulted");
795
796 if (-1 == gpioval)
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500797 {
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600798 error("Failed reading regulator-standby-faulted GPIO");
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500799 }
Andrew Geisslerf8ae6a02022-01-21 17:00:20 -0600800
801 if (1 == gpioval)
802 {
803 info("Detected standby voltage regulator fault");
804 regulatorFault = true;
805 }
806
Ben Tyner2c36e5a2021-07-12 14:56:49 -0500807 return regulatorFault;
808}
809
Andrew Geisslera90a31a2016-12-13 16:16:28 -0600810} // namespace manager
811} // namespace state
Andrew Geisslera965cf02018-08-31 08:37:05 -0700812} // namespace phosphor