blob: e14de4e6a3b6ceee2f3161e4f7ddc59c31b9c985 [file] [log] [blame]
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001/*
Jason M. Bills35471132025-05-06 12:26:10 -07002// Copyright (c) 2018-2025 Intel Corporation
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +030016#include "power_control.hpp"
17
Ed Tanousf61ca6f2019-08-15 15:09:05 -070018#include <sys/sysinfo.h>
19#include <systemd/sd-journal.h>
20
Ed Tanous744e9a92023-02-28 13:35:22 -080021#include <boost/asio/io_context.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070022#include <boost/asio/posix/stream_descriptor.hpp>
Jason M. Billse63dea02020-08-27 12:07:35 -070023#include <boost/asio/steady_timer.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070024#include <boost/container/flat_map.hpp>
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070025#include <boost/container/flat_set.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070026#include <gpiod.hpp>
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053027#include <nlohmann/json.hpp>
Jason M. Billsc46ebb42021-11-10 11:41:31 -080028#include <phosphor-logging/lg2.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070029#include <sdbusplus/asio/object_server.hpp>
Vijay Khemka2b6f4422020-05-29 11:13:23 -070030
31#include <filesystem>
32#include <fstream>
Zev Weissca478552024-06-11 23:45:58 +000033#include <optional>
34#include <regex>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070035#include <string_view>
36
37namespace power_control
38{
Ed Tanous744e9a92023-02-28 13:35:22 -080039static boost::asio::io_context io;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070040std::shared_ptr<sdbusplus::asio::connection> conn;
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +030041PersistentState appState;
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +030042PowerRestoreController powerRestore(io);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053043
44static std::string node = "0";
Andrei Kartashev3efcf372021-12-29 15:32:17 +030045static const std::string appName = "power-control";
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053046
Priyatharshan P70120512020-09-16 18:47:20 +053047enum class DbusConfigType
48{
49 name = 1,
50 path,
51 interface,
52 property
53};
Zev Weissca478552024-06-11 23:45:58 +000054
55// Mandatory config parameters for dbus inputs
Priyatharshan P70120512020-09-16 18:47:20 +053056boost::container::flat_map<DbusConfigType, std::string> dbusParams = {
57 {DbusConfigType::name, "DbusName"},
58 {DbusConfigType::path, "Path"},
59 {DbusConfigType::interface, "Interface"},
60 {DbusConfigType::property, "Property"}};
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053061
Priyatharshan P70120512020-09-16 18:47:20 +053062enum class ConfigType
63{
64 GPIO = 1,
65 DBUS
66};
67
68struct ConfigData
69{
70 std::string name;
71 std::string lineName;
72 std::string dbusName;
73 std::string path;
74 std::string interface;
Zev Weissca478552024-06-11 23:45:58 +000075 std::optional<std::regex> matchRegex;
Jean-Marie Verdun50937e72021-08-31 09:15:49 -070076 bool polarity;
Priyatharshan P70120512020-09-16 18:47:20 +053077 ConfigType type;
78};
79
80static ConfigData powerOutConfig;
81static ConfigData powerOkConfig;
82static ConfigData resetOutConfig;
83static ConfigData nmiOutConfig;
84static ConfigData sioPwrGoodConfig;
85static ConfigData sioOnControlConfig;
86static ConfigData sioS5Config;
87static ConfigData postCompleteConfig;
88static ConfigData powerButtonConfig;
89static ConfigData resetButtonConfig;
90static ConfigData idButtonConfig;
91static ConfigData nmiButtonConfig;
Naveen Moses117c34e2021-05-26 20:10:51 +053092static ConfigData slotPowerConfig;
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +030093static ConfigData hpmStbyEnConfig;
Naveen Moses117c34e2021-05-26 20:10:51 +053094
Priyatharshan P70120512020-09-16 18:47:20 +053095// map for storing list of gpio parameters whose config are to be read from x86
96// power control json config
97boost::container::flat_map<std::string, ConfigData*> powerSignalMap = {
98 {"PowerOut", &powerOutConfig},
99 {"PowerOk", &powerOkConfig},
100 {"ResetOut", &resetOutConfig},
101 {"NMIOut", &nmiOutConfig},
102 {"SioPowerGood", &sioPwrGoodConfig},
103 {"SioOnControl", &sioOnControlConfig},
104 {"SIOS5", &sioS5Config},
105 {"PostComplete", &postCompleteConfig},
106 {"PowerButton", &powerButtonConfig},
107 {"ResetButton", &resetButtonConfig},
108 {"IdButton", &idButtonConfig},
Naveen Moses117c34e2021-05-26 20:10:51 +0530109 {"NMIButton", &nmiButtonConfig},
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +0300110 {"SlotPower", &slotPowerConfig},
111 {"HpmStbyEn", &hpmStbyEnConfig}};
Priyatharshan P70120512020-09-16 18:47:20 +0530112
113static std::string hostDbusName = "xyz.openbmc_project.State.Host";
114static std::string chassisDbusName = "xyz.openbmc_project.State.Chassis";
115static std::string osDbusName = "xyz.openbmc_project.State.OperatingSystem";
116static std::string buttonDbusName = "xyz.openbmc_project.Chassis.Buttons";
117static std::string nmiDbusName = "xyz.openbmc_project.Control.Host.NMI";
118static std::string rstCauseDbusName =
119 "xyz.openbmc_project.Control.Host.RestartCause";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700120static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
121static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
Vijay Khemka04175c22020-10-09 14:28:11 -0700122#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -0700123static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSysIface;
Naveen Moses117c34e2021-05-26 20:10:51 +0530124static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSlotIface;
Vijay Khemka04175c22020-10-09 14:28:11 -0700125#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700126static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
127static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
128static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
129static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
130static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
Chen Yugang174ec662019-08-19 19:58:49 +0800131static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700132static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700133
134static gpiod::line powerButtonMask;
135static gpiod::line resetButtonMask;
136static bool nmiButtonMasked = false;
Matt Simmering58e379d2022-09-23 14:45:50 -0700137#if IGNORE_SOFT_RESETS_DURING_POST
138static bool ignoreNextSoftReset = false;
139#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700140
Priyatharshan P70120512020-09-16 18:47:20 +0530141// This map contains all timer values that are to be read from json config
142boost::container::flat_map<std::string, int> TimerMap = {
Jason M. Billsaeefe042021-09-08 14:56:11 -0700143 {"PowerPulseMs", 200},
144 {"ForceOffPulseMs", 15000},
145 {"ResetPulseMs", 500},
146 {"PowerCycleMs", 5000},
147 {"SioPowerGoodWatchdogMs", 1000},
Jason M. Bills35471132025-05-06 12:26:10 -0700148 {"PowerOKWatchdogMs", 8000},
Jason M. Billsaeefe042021-09-08 14:56:11 -0700149 {"GracefulPowerOffS", (5 * 60)},
150 {"WarmResetCheckMs", 500},
151 {"PowerOffSaveMs", 7000},
Michal Orzeledd211e2022-10-28 13:10:16 +0200152 {"SlotPowerCycleMs", 200},
153 {"DbusGetPropertyRetry", 1000}};
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700154
155static bool nmiEnabled = true;
Olivier FAURAXd7ea2832023-04-14 14:08:02 +0000156static bool nmiWhenPoweredOff = true;
Priyatharshan P19c47a32020-08-12 18:16:43 +0530157static bool sioEnabled = true;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700158
159// Timers
160// Time holding GPIOs asserted
161static boost::asio::steady_timer gpioAssertTimer(io);
162// Time between off and on during a power cycle
163static boost::asio::steady_timer powerCycleTimer(io);
164// Time OS gracefully powering off
165static boost::asio::steady_timer gracefulPowerOffTimer(io);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700166// Time the warm reset check
167static boost::asio::steady_timer warmResetCheckTimer(io);
Jason M. Bills35471132025-05-06 12:26:10 -0700168// Time power OK assertion on power-on
169static boost::asio::steady_timer powerOKWatchdogTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700170// Time SIO power good assertion on power-on
171static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
172// Time power-off state save for power loss tracking
173static boost::asio::steady_timer powerStateSaveTimer(io);
174// POH timer
175static boost::asio::steady_timer pohCounterTimer(io);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700176// Time when to allow restart cause updates
177static boost::asio::steady_timer restartCauseTimer(io);
Naveen Moses117c34e2021-05-26 20:10:51 +0530178static boost::asio::steady_timer slotPowerCycleTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700179
Michal Orzeledd211e2022-10-28 13:10:16 +0200180// Map containing timers used for D-Bus get-property retries
181static boost::container::flat_map<std::string, boost::asio::steady_timer>
182 dBusRetryTimers;
183
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700184// GPIO Lines and Event Descriptors
Jason M. Bills35471132025-05-06 12:26:10 -0700185static gpiod::line powerOKLine;
186static boost::asio::posix::stream_descriptor powerOKEvent(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700187static gpiod::line sioPowerGoodLine;
188static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
189static gpiod::line sioOnControlLine;
190static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
191static gpiod::line sioS5Line;
192static boost::asio::posix::stream_descriptor sioS5Event(io);
193static gpiod::line powerButtonLine;
194static boost::asio::posix::stream_descriptor powerButtonEvent(io);
195static gpiod::line resetButtonLine;
196static boost::asio::posix::stream_descriptor resetButtonEvent(io);
197static gpiod::line nmiButtonLine;
198static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
199static gpiod::line idButtonLine;
200static boost::asio::posix::stream_descriptor idButtonEvent(io);
201static gpiod::line postCompleteLine;
202static boost::asio::posix::stream_descriptor postCompleteEvent(io);
203static gpiod::line nmiOutLine;
Naveen Moses117c34e2021-05-26 20:10:51 +0530204static gpiod::line slotPowerLine;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700205
206static constexpr uint8_t beepPowerFail = 8;
207
208static void beep(const uint8_t& beepPriority)
209{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800210 lg2::info("Beep with priority: {BEEP_PRIORITY}", "BEEP_PRIORITY",
211 beepPriority);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700212
213 conn->async_method_call(
214 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400215 if (ec)
216 {
217 lg2::error(
218 "beep returned error with async_method_call (ec = {ERROR_MSG})",
219 "ERROR_MSG", ec.message());
220 return;
221 }
222 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700223 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
224 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
225}
226
Tim Lee86239182021-12-23 11:46:01 +0800227enum class OperatingSystemStateStage
228{
229 Inactive,
230 Standby,
231};
Matt Simmering58e379d2022-09-23 14:45:50 -0700232static OperatingSystemStateStage operatingSystemState;
Patrick Williamsb84e7892025-02-01 08:22:06 -0500233static constexpr std::string_view getOperatingSystemStateStage(
234 const OperatingSystemStateStage stage)
Tim Lee86239182021-12-23 11:46:01 +0800235{
236 switch (stage)
237 {
238 case OperatingSystemStateStage::Inactive:
239 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
240 break;
241 case OperatingSystemStateStage::Standby:
242 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby";
243 break;
244 default:
245 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
246 break;
247 }
248};
249static void setOperatingSystemState(const OperatingSystemStateStage stage)
250{
Matt Simmering58e379d2022-09-23 14:45:50 -0700251 operatingSystemState = stage;
252#if IGNORE_SOFT_RESETS_DURING_POST
253 // If POST complete has asserted set ignoreNextSoftReset to false to avoid
254 // masking soft resets after POST
255 if (operatingSystemState == OperatingSystemStateStage::Standby)
256 {
257 ignoreNextSoftReset = false;
258 }
259#endif
Tim Lee86239182021-12-23 11:46:01 +0800260 osIface->set_property("OperatingSystemState",
261 std::string(getOperatingSystemStateStage(stage)));
262
263 lg2::info("Moving os state to {STATE} stage", "STATE",
264 getOperatingSystemStateStage(stage));
265}
266
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700267enum class PowerState
268{
269 on,
Jason M. Bills35471132025-05-06 12:26:10 -0700270 waitForPowerOK,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700271 waitForSIOPowerGood,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700272 off,
273 transitionToOff,
274 gracefulTransitionToOff,
275 cycleOff,
276 transitionToCycleOff,
277 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700278 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700279};
280static PowerState powerState;
281static std::string getPowerStateName(PowerState state)
282{
283 switch (state)
284 {
285 case PowerState::on:
286 return "On";
287 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700288 case PowerState::waitForPowerOK:
289 return "Wait for Power OK";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700290 break;
291 case PowerState::waitForSIOPowerGood:
292 return "Wait for SIO Power Good";
293 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700294 case PowerState::off:
295 return "Off";
296 break;
297 case PowerState::transitionToOff:
298 return "Transition to Off";
299 break;
300 case PowerState::gracefulTransitionToOff:
301 return "Graceful Transition to Off";
302 break;
303 case PowerState::cycleOff:
304 return "Power Cycle Off";
305 break;
306 case PowerState::transitionToCycleOff:
307 return "Transition to Power Cycle Off";
308 break;
309 case PowerState::gracefulTransitionToCycleOff:
310 return "Graceful Transition to Power Cycle Off";
311 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700312 case PowerState::checkForWarmReset:
313 return "Check for Warm Reset";
314 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700315 default:
316 return "unknown state: " + std::to_string(static_cast<int>(state));
317 break;
318 }
319}
320static void logStateTransition(const PowerState state)
321{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800322 lg2::info("Host{HOST}: Moving to \"{STATE}\" state", "HOST", node, "STATE",
323 getPowerStateName(state));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700324}
325
326enum class Event
327{
Jason M. Bills35471132025-05-06 12:26:10 -0700328 powerOKAssert,
329 powerOKDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700330 sioPowerGoodAssert,
331 sioPowerGoodDeAssert,
332 sioS5Assert,
333 sioS5DeAssert,
Jason M. Billsfb957332021-01-28 13:18:46 -0800334 pltRstAssert,
335 pltRstDeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700336 postCompleteAssert,
337 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700338 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700339 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700340 powerCycleTimerExpired,
Jason M. Bills35471132025-05-06 12:26:10 -0700341 powerOKWatchdogTimerExpired,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700342 sioPowerGoodWatchdogTimerExpired,
343 gracefulPowerOffTimerExpired,
344 powerOnRequest,
345 powerOffRequest,
346 powerCycleRequest,
347 resetRequest,
348 gracefulPowerOffRequest,
349 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700350 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700351};
352static std::string getEventName(Event event)
353{
354 switch (event)
355 {
Jason M. Bills35471132025-05-06 12:26:10 -0700356 case Event::powerOKAssert:
357 return "power OK assert";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700358 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700359 case Event::powerOKDeAssert:
360 return "power OK de-assert";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700361 break;
362 case Event::sioPowerGoodAssert:
363 return "SIO power good assert";
364 break;
365 case Event::sioPowerGoodDeAssert:
366 return "SIO power good de-assert";
367 break;
368 case Event::sioS5Assert:
369 return "SIO S5 assert";
370 break;
371 case Event::sioS5DeAssert:
372 return "SIO S5 de-assert";
373 break;
Jason M. Billsfb957332021-01-28 13:18:46 -0800374 case Event::pltRstAssert:
375 return "PLT_RST assert";
376 break;
377 case Event::pltRstDeAssert:
378 return "PLT_RST de-assert";
379 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700380 case Event::postCompleteAssert:
381 return "POST Complete assert";
382 break;
383 case Event::postCompleteDeAssert:
384 return "POST Complete de-assert";
385 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700386 case Event::powerButtonPressed:
387 return "power button pressed";
388 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700389 case Event::resetButtonPressed:
390 return "reset button pressed";
391 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700392 case Event::powerCycleTimerExpired:
393 return "power cycle timer expired";
394 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700395 case Event::powerOKWatchdogTimerExpired:
396 return "power OK watchdog timer expired";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700397 break;
398 case Event::sioPowerGoodWatchdogTimerExpired:
399 return "SIO power good watchdog timer expired";
400 break;
401 case Event::gracefulPowerOffTimerExpired:
402 return "graceful power-off timer expired";
403 break;
404 case Event::powerOnRequest:
405 return "power-on request";
406 break;
407 case Event::powerOffRequest:
408 return "power-off request";
409 break;
410 case Event::powerCycleRequest:
411 return "power-cycle request";
412 break;
413 case Event::resetRequest:
414 return "reset request";
415 break;
416 case Event::gracefulPowerOffRequest:
417 return "graceful power-off request";
418 break;
419 case Event::gracefulPowerCycleRequest:
420 return "graceful power-cycle request";
421 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700422 case Event::warmResetDetected:
423 return "warm reset detected";
424 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700425 default:
426 return "unknown event: " + std::to_string(static_cast<int>(event));
427 break;
428 }
429}
430static void logEvent(const std::string_view stateHandler, const Event event)
431{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800432 lg2::info("{STATE_HANDLER}: {EVENT} event received", "STATE_HANDLER",
433 stateHandler, "EVENT", getEventName(event));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700434}
435
436// Power state handlers
437static void powerStateOn(const Event event);
Jason M. Bills35471132025-05-06 12:26:10 -0700438static void powerStateWaitForPowerOK(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700439static void powerStateWaitForSIOPowerGood(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700440static void powerStateOff(const Event event);
441static void powerStateTransitionToOff(const Event event);
442static void powerStateGracefulTransitionToOff(const Event event);
443static void powerStateCycleOff(const Event event);
444static void powerStateTransitionToCycleOff(const Event event);
445static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700446static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700447
448static std::function<void(const Event)> getPowerStateHandler(PowerState state)
449{
450 switch (state)
451 {
452 case PowerState::on:
453 return powerStateOn;
454 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700455 case PowerState::waitForPowerOK:
456 return powerStateWaitForPowerOK;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700457 break;
458 case PowerState::waitForSIOPowerGood:
459 return powerStateWaitForSIOPowerGood;
460 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700461 case PowerState::off:
462 return powerStateOff;
463 break;
464 case PowerState::transitionToOff:
465 return powerStateTransitionToOff;
466 break;
467 case PowerState::gracefulTransitionToOff:
468 return powerStateGracefulTransitionToOff;
469 break;
470 case PowerState::cycleOff:
471 return powerStateCycleOff;
472 break;
473 case PowerState::transitionToCycleOff:
474 return powerStateTransitionToCycleOff;
475 break;
476 case PowerState::gracefulTransitionToCycleOff:
477 return powerStateGracefulTransitionToCycleOff;
478 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700479 case PowerState::checkForWarmReset:
480 return powerStateCheckForWarmReset;
481 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700482 default:
Zev Weiss047bcb52020-08-20 21:28:11 +0000483 return nullptr;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700484 break;
485 }
486};
487
488static void sendPowerControlEvent(const Event event)
489{
490 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
491 if (handler == nullptr)
492 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800493 lg2::error("Failed to find handler for power state: {STATE}", "STATE",
494 static_cast<int>(powerState));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700495 return;
496 }
497 handler(event);
498}
499
500static uint64_t getCurrentTimeMs()
501{
502 struct timespec time = {};
503
504 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
505 {
506 return 0;
507 }
508 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
509 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
510
511 return currentTimeMs;
512}
513
514static constexpr std::string_view getHostState(const PowerState state)
515{
516 switch (state)
517 {
518 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700519 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700520 case PowerState::gracefulTransitionToCycleOff:
521 return "xyz.openbmc_project.State.Host.HostState.Running";
522 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700523 case PowerState::waitForPowerOK:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700524 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700525 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700526 case PowerState::transitionToOff:
527 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700528 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700529 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700530 return "xyz.openbmc_project.State.Host.HostState.Off";
531 break;
532 default:
533 return "";
534 break;
535 }
536};
537static constexpr std::string_view getChassisState(const PowerState state)
538{
539 switch (state)
540 {
541 case PowerState::on:
542 case PowerState::transitionToOff:
543 case PowerState::gracefulTransitionToOff:
544 case PowerState::transitionToCycleOff:
545 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700546 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700547 return "xyz.openbmc_project.State.Chassis.PowerState.On";
548 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700549 case PowerState::waitForPowerOK:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700550 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700551 case PowerState::off:
552 case PowerState::cycleOff:
553 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
554 break;
555 default:
556 return "";
557 break;
558 }
559};
Naveen Moses117c34e2021-05-26 20:10:51 +0530560#ifdef CHASSIS_SYSTEM_RESET
561enum class SlotPowerState
562{
563 on,
564 off,
565};
566static SlotPowerState slotPowerState;
567static constexpr std::string_view getSlotState(const SlotPowerState state)
568{
569 switch (state)
570 {
571 case SlotPowerState::on:
572 return "xyz.openbmc_project.State.Chassis.PowerState.On";
573 break;
574 case SlotPowerState::off:
575 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
576 break;
577 default:
578 return "";
579 break;
580 }
581};
582static void setSlotPowerState(const SlotPowerState state)
583{
584 slotPowerState = state;
585 chassisSlotIface->set_property("CurrentPowerState",
586 std::string(getSlotState(slotPowerState)));
587 chassisSlotIface->set_property("LastStateChangeTime", getCurrentTimeMs());
588}
589#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700590static void savePowerState(const PowerState state)
591{
592 powerStateSaveTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -0700593 std::chrono::milliseconds(TimerMap["PowerOffSaveMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700594 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
595 if (ec)
596 {
597 // operation_aborted is expected if timer is canceled before
598 // completion.
599 if (ec != boost::asio::error::operation_aborted)
600 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800601 lg2::error("Power-state save async_wait failed: {ERROR_MSG}",
602 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700603 }
604 return;
605 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300606 appState.set(PersistentState::Params::PowerState,
607 std::string{getChassisState(state)});
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700608 });
609}
610static void setPowerState(const PowerState state)
611{
612 powerState = state;
613 logStateTransition(state);
614
615 hostIface->set_property("CurrentHostState",
616 std::string(getHostState(powerState)));
617
618 chassisIface->set_property("CurrentPowerState",
619 std::string(getChassisState(powerState)));
620 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
621
622 // Save the power state for the restore policy
623 savePowerState(state);
624}
625
626enum class RestartCause
627{
628 command,
629 resetButton,
630 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700631 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700632 powerPolicyOn,
633 powerPolicyRestore,
634 softReset,
635};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700636static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700637static std::string getRestartCause(RestartCause cause)
638{
639 switch (cause)
640 {
641 case RestartCause::command:
642 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
643 break;
644 case RestartCause::resetButton:
645 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
646 break;
647 case RestartCause::powerButton:
648 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
649 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700650 case RestartCause::watchdog:
651 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
652 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700653 case RestartCause::powerPolicyOn:
Jason M. Bills418ce112021-09-08 15:15:05 -0700654 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700655 break;
656 case RestartCause::powerPolicyRestore:
Jason M. Bills418ce112021-09-08 15:15:05 -0700657 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyPreviousState";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700658 break;
659 case RestartCause::softReset:
660 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
661 break;
662 default:
663 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
664 break;
665 }
666}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700667static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700668{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700669 // Add this to the set of causes for this restart
670 causeSet.insert(cause);
671}
672static void clearRestartCause()
673{
674 // Clear the set for the next restart
675 causeSet.clear();
676}
677static void setRestartCauseProperty(const std::string& cause)
678{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800679 lg2::info("RestartCause set to {RESTART_CAUSE}", "RESTART_CAUSE", cause);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700680 restartCauseIface->set_property("RestartCause", cause);
681}
Rashmi RV89f61312020-01-22 15:41:50 +0530682
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300683#ifdef USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530684static void resetACBootProperty()
685{
686 if ((causeSet.contains(RestartCause::command)) ||
687 (causeSet.contains(RestartCause::softReset)))
688 {
689 conn->async_method_call(
690 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400691 if (ec)
692 {
693 lg2::error("failed to reset ACBoot property");
694 }
695 },
Rashmi RV89f61312020-01-22 15:41:50 +0530696 "xyz.openbmc_project.Settings",
697 "/xyz/openbmc_project/control/host0/ac_boot",
698 "org.freedesktop.DBus.Properties", "Set",
699 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
700 std::variant<std::string>{"False"});
701 }
702}
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300703#endif // USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530704
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700705static void setRestartCause()
706{
707 // Determine the actual restart cause based on the set of causes
708 std::string restartCause =
709 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
710 if (causeSet.contains(RestartCause::watchdog))
711 {
712 restartCause = getRestartCause(RestartCause::watchdog);
713 }
714 else if (causeSet.contains(RestartCause::command))
715 {
716 restartCause = getRestartCause(RestartCause::command);
717 }
718 else if (causeSet.contains(RestartCause::resetButton))
719 {
720 restartCause = getRestartCause(RestartCause::resetButton);
721 }
722 else if (causeSet.contains(RestartCause::powerButton))
723 {
724 restartCause = getRestartCause(RestartCause::powerButton);
725 }
726 else if (causeSet.contains(RestartCause::powerPolicyOn))
727 {
728 restartCause = getRestartCause(RestartCause::powerPolicyOn);
729 }
730 else if (causeSet.contains(RestartCause::powerPolicyRestore))
731 {
732 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
733 }
734 else if (causeSet.contains(RestartCause::softReset))
735 {
Matt Simmering58e379d2022-09-23 14:45:50 -0700736#if IGNORE_SOFT_RESETS_DURING_POST
737 if (ignoreNextSoftReset)
738 {
739 ignoreNextSoftReset = false;
740 return;
741 }
742#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700743 restartCause = getRestartCause(RestartCause::softReset);
744 }
745
746 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700747}
748
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700749static void systemPowerGoodFailedLog()
750{
751 sd_journal_send(
752 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
753 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
754 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
Jason M. Billsaeefe042021-09-08 14:56:11 -0700755 TimerMap["SioPowerGoodWatchdogMs"], NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700756}
757
Jason M. Bills35471132025-05-06 12:26:10 -0700758static void powerOKFailedLog()
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700759{
Jason M. Bills35471132025-05-06 12:26:10 -0700760 sd_journal_send("MESSAGE=PowerControl: power okay failed to assert",
761 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
762 "OpenBMC.0.1.SystemPowerOnFailed", NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700763}
764
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700765static void powerRestorePolicyLog()
766{
767 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
768 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
769 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
770}
771
772static void powerButtonPressLog()
773{
774 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
775 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
776 "OpenBMC.0.1.PowerButtonPressed", NULL);
777}
778
779static void resetButtonPressLog()
780{
781 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
782 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
783 "OpenBMC.0.1.ResetButtonPressed", NULL);
784}
785
786static void nmiButtonPressLog()
787{
788 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
789 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
790 "OpenBMC.0.1.NMIButtonPressed", NULL);
791}
792
793static void nmiDiagIntLog()
794{
795 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
796 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
797 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
798}
799
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300800PersistentState::PersistentState()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700801{
802 // create the power control directory if it doesn't exist
803 std::error_code ec;
804 if (!(std::filesystem::create_directories(powerControlDir, ec)))
805 {
806 if (ec.value() != 0)
807 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800808 lg2::error("failed to create {DIR_NAME}: {ERROR_MSG}", "DIR_NAME",
809 powerControlDir.string(), "ERROR_MSG", ec.message());
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300810 throw std::runtime_error("Failed to create state directory");
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700811 }
812 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300813
814 // read saved state, it's ok, if the file doesn't exists
815 std::ifstream appStateStream(powerControlDir / stateFile);
816 if (!appStateStream.is_open())
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700817 {
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300818 lg2::info("Cannot open state file \'{PATH}\'", "PATH",
819 std::string(powerControlDir / stateFile));
820 stateData = nlohmann::json({});
821 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700822 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300823 try
824 {
825 appStateStream >> stateData;
826 if (stateData.is_discarded())
827 {
828 lg2::info("Cannot parse state file \'{PATH}\'", "PATH",
829 std::string(powerControlDir / stateFile));
830 stateData = nlohmann::json({});
831 return;
832 }
833 }
834 catch (const std::exception& ex)
835 {
836 lg2::info("Cannot read state file \'{PATH}\'", "PATH",
837 std::string(powerControlDir / stateFile));
838 stateData = nlohmann::json({});
839 return;
840 }
841}
842PersistentState::~PersistentState()
843{
844 saveState();
845}
846const std::string PersistentState::get(Params parameter)
847{
848 auto val = stateData.find(getName(parameter));
849 if (val != stateData.end())
850 {
851 return val->get<std::string>();
852 }
853 return getDefault(parameter);
854}
855void PersistentState::set(Params parameter, const std::string& value)
856{
857 stateData[getName(parameter)] = value;
858 saveState();
859}
860
861const std::string PersistentState::getName(const Params parameter)
862{
863 switch (parameter)
864 {
865 case Params::PowerState:
866 return "PowerState";
867 }
868 return "";
869}
870const std::string PersistentState::getDefault(const Params parameter)
871{
872 switch (parameter)
873 {
874 case Params::PowerState:
875 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
876 }
877 return "";
878}
879void PersistentState::saveState()
880{
881 std::ofstream appStateStream(powerControlDir / stateFile, std::ios::trunc);
882 if (!appStateStream.is_open())
883 {
884 lg2::error("Cannot write state file \'{PATH}\'", "PATH",
885 std::string(powerControlDir / stateFile));
886 return;
887 }
888 appStateStream << stateData.dump(indentationSize);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700889}
890
Patrick Williams48aa1f02023-05-10 07:50:30 -0500891static constexpr const char* setingsService = "xyz.openbmc_project.Settings";
892static constexpr const char* powerRestorePolicyIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300893 "xyz.openbmc_project.Control.Power.RestorePolicy";
894#ifdef USE_ACBOOT
Patrick Williams48aa1f02023-05-10 07:50:30 -0500895static constexpr const char* powerACBootObject =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300896 "/xyz/openbmc_project/control/host0/ac_boot";
Patrick Williams48aa1f02023-05-10 07:50:30 -0500897static constexpr const char* powerACBootIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300898 "xyz.openbmc_project.Common.ACBoot";
899#endif // USE_ACBOOT
900
901namespace match_rules = sdbusplus::bus::match::rules;
902
903static int powerRestoreConfigHandler(sd_bus_message* m, void* context,
904 sd_bus_error*)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700905{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300906 if (context == nullptr || m == nullptr)
907 {
908 throw std::runtime_error("Invalid match");
909 }
Patrick Williams439b9c32022-07-22 19:26:53 -0500910 sdbusplus::message_t message(m);
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300911 PowerRestoreController* powerRestore =
912 static_cast<PowerRestoreController*>(context);
913
914 if (std::string(message.get_member()) == "InterfacesAdded")
915 {
916 sdbusplus::message::object_path path;
917 boost::container::flat_map<std::string, dbusPropertiesList> data;
918
919 message.read(path, data);
920
921 for (auto& [iface, properties] : data)
922 {
923 if ((iface == powerRestorePolicyIface)
924#ifdef USE_ACBOOT
925 || (iface == powerACBootIface)
926#endif // USE_ACBOOT
927 )
928 {
929 powerRestore->setProperties(properties);
930 }
931 }
932 }
933 else if (std::string(message.get_member()) == "PropertiesChanged")
934 {
935 std::string interfaceName;
936 dbusPropertiesList propertiesChanged;
937
938 message.read(interfaceName, propertiesChanged);
939
940 powerRestore->setProperties(propertiesChanged);
941 }
942 return 1;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700943}
944
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300945void PowerRestoreController::run()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700946{
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400947 std::string powerRestorePolicyObject =
948 "/xyz/openbmc_project/control/host" + node + "/power_restore_policy";
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300949 powerRestorePolicyLog();
950 // this list only needs to be created once
951 if (matches.empty())
952 {
953 matches.emplace_back(
954 *conn,
955 match_rules::interfacesAdded() +
956 match_rules::argNpath(0, powerRestorePolicyObject) +
957 match_rules::sender(setingsService),
958 powerRestoreConfigHandler, this);
959#ifdef USE_ACBOOT
960 matches.emplace_back(*conn,
961 match_rules::interfacesAdded() +
962 match_rules::argNpath(0, powerACBootObject) +
963 match_rules::sender(setingsService),
964 powerRestoreConfigHandler, this);
965 matches.emplace_back(*conn,
966 match_rules::propertiesChanged(powerACBootObject,
967 powerACBootIface) +
968 match_rules::sender(setingsService),
969 powerRestoreConfigHandler, this);
970#endif // USE_ACBOOT
971 }
972
973 // Check if it's already on DBus
974 conn->async_method_call(
975 [this](boost::system::error_code ec,
976 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400977 if (ec)
978 {
979 return;
980 }
981 setProperties(properties);
982 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300983 setingsService, powerRestorePolicyObject,
984 "org.freedesktop.DBus.Properties", "GetAll", powerRestorePolicyIface);
985
986#ifdef USE_ACBOOT
987 // Check if it's already on DBus
988 conn->async_method_call(
989 [this](boost::system::error_code ec,
990 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400991 if (ec)
992 {
993 return;
994 }
995 setProperties(properties);
996 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300997 setingsService, powerACBootObject, "org.freedesktop.DBus.Properties",
998 "GetAll", powerACBootIface);
999#endif
1000}
1001
1002void PowerRestoreController::setProperties(const dbusPropertiesList& props)
1003{
1004 for (auto& [property, propValue] : props)
1005 {
1006 if (property == "PowerRestorePolicy")
1007 {
1008 const std::string* value = std::get_if<std::string>(&propValue);
1009 if (value == nullptr)
1010 {
1011 lg2::error("Unable to read Power Restore Policy");
1012 continue;
1013 }
1014 powerRestorePolicy = *value;
1015 }
1016 else if (property == "PowerRestoreDelay")
1017 {
1018 const uint64_t* value = std::get_if<uint64_t>(&propValue);
1019 if (value == nullptr)
1020 {
1021 lg2::error("Unable to read Power Restore Delay");
1022 continue;
1023 }
1024 powerRestoreDelay = *value / 1000000; // usec to sec
1025 }
1026#ifdef USE_ACBOOT
1027 else if (property == "ACBoot")
1028 {
1029 const std::string* value = std::get_if<std::string>(&propValue);
1030 if (value == nullptr)
1031 {
1032 lg2::error("Unable to read AC Boot status");
1033 continue;
1034 }
1035 acBoot = *value;
1036 }
1037#endif // USE_ACBOOT
1038 }
1039 invokeIfReady();
1040}
1041
1042void PowerRestoreController::invokeIfReady()
1043{
1044 if ((powerRestorePolicy.empty()) || (powerRestoreDelay < 0))
1045 {
1046 return;
1047 }
1048#ifdef USE_ACBOOT
1049 if (acBoot.empty() || acBoot == "Unknown")
1050 {
1051 return;
1052 }
1053#endif
1054
1055 matches.clear();
1056 if (!timerFired)
1057 {
1058 // Calculate the delay from now to meet the requested delay
1059 // Subtract the approximate uboot time
1060 static constexpr const int ubootSeconds = 20;
1061 int delay = powerRestoreDelay - ubootSeconds;
1062 // Subtract the time since boot
1063 struct sysinfo info = {};
1064 if (sysinfo(&info) == 0)
1065 {
1066 delay -= info.uptime;
1067 }
1068
1069 if (delay > 0)
1070 {
1071 powerRestoreTimer.expires_after(std::chrono::seconds(delay));
1072 lg2::info("Power Restore delay of {DELAY} seconds started", "DELAY",
1073 delay);
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001074 powerRestoreTimer.async_wait([this](const boost::system::error_code
1075 ec) {
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001076 if (ec)
1077 {
1078 // operation_aborted is expected if timer is canceled before
1079 // completion.
1080 if (ec == boost::asio::error::operation_aborted)
1081 {
1082 return;
1083 }
1084 lg2::error(
1085 "power restore policy async_wait failed: {ERROR_MSG}",
1086 "ERROR_MSG", ec.message());
1087 }
1088 else
1089 {
1090 lg2::info("Power Restore delay timer expired");
1091 }
1092 invoke();
1093 });
1094 timerFired = true;
1095 }
1096 else
1097 {
1098 invoke();
1099 }
1100 }
1101}
1102
1103void PowerRestoreController::invoke()
1104{
1105 // we want to run Power Restore only once
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001106 if (policyInvoked)
1107 {
1108 return;
1109 }
1110 policyInvoked = true;
1111
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001112 lg2::info("Invoking Power Restore Policy {POLICY}", "POLICY",
1113 powerRestorePolicy);
1114 if (powerRestorePolicy ==
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001115 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
1116 {
1117 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001118 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001119 }
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001120 else if (powerRestorePolicy ==
Jason M. Bills418ce112021-09-08 15:15:05 -07001121 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001122 {
1123 if (wasPowerDropped())
1124 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001125 lg2::info("Power was dropped, restoring Host On state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001126 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001127 setRestartCauseProperty(
1128 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001129 }
1130 else
1131 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001132 lg2::info("No power drop, restoring Host Off state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001133 }
1134 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -07001135 // We're done with the previous power state for the restore policy, so store
1136 // the current state
1137 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001138}
1139
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001140bool PowerRestoreController::wasPowerDropped()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001141{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001142 std::string state = appState.get(PersistentState::Params::PowerState);
1143 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001144}
1145
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001146static void waitForGPIOEvent(
1147 const std::string& name, const std::function<void(bool)>& eventHandler,
1148 gpiod::line& line, boost::asio::posix::stream_descriptor& event)
Zev Weiss676ef2c2021-09-02 21:54:02 -05001149{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001150 event.async_wait(
1151 boost::asio::posix::stream_descriptor::wait_read,
1152 [&name, eventHandler, &line,
1153 &event](const boost::system::error_code ec) {
1154 if (ec)
1155 {
1156 lg2::error("{GPIO_NAME} fd handler error: {ERROR_MSG}",
1157 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1158 // TODO: throw here to force power-control to
1159 // restart?
1160 return;
1161 }
1162 gpiod::line_event line_event = line.event_read();
1163 eventHandler(line_event.event_type ==
1164 gpiod::line_event::RISING_EDGE);
1165 waitForGPIOEvent(name, eventHandler, line, event);
1166 });
Zev Weiss676ef2c2021-09-02 21:54:02 -05001167}
1168
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001169static bool requestGPIOEvents(
Zev Weiss676ef2c2021-09-02 21:54:02 -05001170 const std::string& name, const std::function<void(bool)>& handler,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001171 gpiod::line& gpioLine,
1172 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1173{
1174 // Find the GPIO line
1175 gpioLine = gpiod::find_line(name);
1176 if (!gpioLine)
1177 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001178 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001179 return false;
1180 }
1181
1182 try
1183 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001184 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001185 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001186 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001187 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001188 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}",
1189 "GPIO_NAME", name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001190 return false;
1191 }
1192
1193 int gpioLineFd = gpioLine.event_get_fd();
1194 if (gpioLineFd < 0)
1195 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001196 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001197 return false;
1198 }
1199
1200 gpioEventDescriptor.assign(gpioLineFd);
1201
Zev Weiss676ef2c2021-09-02 21:54:02 -05001202 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001203 return true;
1204}
1205
1206static bool setGPIOOutput(const std::string& name, const int value,
1207 gpiod::line& gpioLine)
1208{
1209 // Find the GPIO line
1210 gpioLine = gpiod::find_line(name);
1211 if (!gpioLine)
1212 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001213 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001214 return false;
1215 }
1216
1217 // Request GPIO output to specified value
1218 try
1219 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001220 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}},
1221 value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001222 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001223 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001224 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001225 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
1226 name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001227 return false;
1228 }
1229
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001230 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1231 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001232 return true;
1233}
1234
1235static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1236 const std::string& name, const int value,
1237 const int durationMs)
1238{
1239 // Set the masked GPIO line to the specified value
1240 maskedGPIOLine.set_value(value);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001241 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1242 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001243 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001244 gpioAssertTimer.async_wait(
1245 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001246 // Set the masked GPIO line back to the opposite value
1247 maskedGPIOLine.set_value(!value);
1248 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1249 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001250 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001251 // operation_aborted is expected if timer is canceled before
1252 // completion.
1253 if (ec != boost::asio::error::operation_aborted)
1254 {
1255 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1256 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1257 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001258 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001259 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001260 return 0;
1261}
1262
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001263static int setGPIOOutputForMs(const ConfigData& config, const int value,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001264 const int durationMs)
1265{
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001266 // If the requested GPIO is masked, use the mask line to set the output
1267 if (powerButtonMask && config.lineName == powerOutConfig.lineName)
1268 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001269 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value,
1270 durationMs);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001271 }
1272 if (resetButtonMask && config.lineName == resetOutConfig.lineName)
1273 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001274 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value,
1275 durationMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001276 }
1277
1278 // No mask set, so request and set the GPIO normally
1279 gpiod::line gpioLine;
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001280 if (!setGPIOOutput(config.lineName, value, gpioLine))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001281 {
1282 return -1;
1283 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001284 const std::string name = config.lineName;
Jean-Marie Verdun2c495cf2021-09-17 21:42:23 -04001285
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001286 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001287 gpioAssertTimer.async_wait(
1288 [gpioLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001289 // Set the GPIO line back to the opposite value
1290 gpioLine.set_value(!value);
1291 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1292 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001293 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001294 // operation_aborted is expected if timer is canceled before
1295 // completion.
1296 if (ec != boost::asio::error::operation_aborted)
1297 {
1298 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1299 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1300 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001301 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001302 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001303 return 0;
1304}
1305
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001306static int assertGPIOForMs(const ConfigData& config, const int durationMs)
1307{
1308 return setGPIOOutputForMs(config, config.polarity, durationMs);
1309}
1310
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001311static void powerOn()
1312{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001313 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001314}
Naveen Moses117c34e2021-05-26 20:10:51 +05301315#ifdef CHASSIS_SYSTEM_RESET
1316static int slotPowerOn()
1317{
1318 if (power_control::slotPowerState != power_control::SlotPowerState::on)
1319 {
Naveen Moses117c34e2021-05-26 20:10:51 +05301320 slotPowerLine.set_value(1);
1321
1322 if (slotPowerLine.get_value() > 0)
1323 {
1324 setSlotPowerState(SlotPowerState::on);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001325 lg2::info("Slot Power is switched On\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301326 }
1327 else
1328 {
1329 return -1;
1330 }
1331 }
1332 else
1333 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001334 lg2::info("Slot Power is already in 'On' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301335 return -1;
1336 }
1337 return 0;
1338}
1339static int slotPowerOff()
1340{
1341 if (power_control::slotPowerState != power_control::SlotPowerState::off)
1342 {
1343 slotPowerLine.set_value(0);
1344
1345 if (!(slotPowerLine.get_value() > 0))
1346 {
1347 setSlotPowerState(SlotPowerState::off);
1348 setPowerState(PowerState::off);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001349 lg2::info("Slot Power is switched Off\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301350 }
1351 else
1352 {
1353 return -1;
1354 }
1355 }
1356 else
1357 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001358 lg2::info("Slot Power is already in 'Off' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301359 return -1;
1360 }
1361 return 0;
1362}
1363static void slotPowerCycle()
1364{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001365 lg2::info("Slot Power Cycle started\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301366 slotPowerOff();
1367 slotPowerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001368 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"]));
Naveen Moses117c34e2021-05-26 20:10:51 +05301369 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) {
1370 if (ec)
1371 {
1372 if (ec != boost::asio::error::operation_aborted)
1373 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001374 lg2::error(
1375 "Slot Power cycle timer async_wait failed: {ERROR_MSG}",
1376 "ERROR_MSG", ec.message());
Naveen Moses117c34e2021-05-26 20:10:51 +05301377 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001378 lg2::info("Slot Power cycle timer canceled\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301379 return;
1380 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001381 lg2::info("Slot Power cycle timer completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301382 slotPowerOn();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001383 lg2::info("Slot Power Cycle Completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301384 });
1385}
1386#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001387static void gracefulPowerOff()
1388{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001389 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001390}
1391
1392static void forcePowerOff()
1393{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001394 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001395 {
1396 return;
1397 }
1398
Jason M. Billsc6961b62021-10-21 14:08:02 -07001399 // If the force off timer expires, then the power-button override failed
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001400 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1401 if (ec)
1402 {
1403 // operation_aborted is expected if timer is canceled before
1404 // completion.
1405 if (ec != boost::asio::error::operation_aborted)
1406 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001407 lg2::error("Force power off async_wait failed: {ERROR_MSG}",
1408 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001409 }
1410 return;
1411 }
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001412
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001413 lg2::error("Power-button override failed. Not sure what to do now.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001414 });
1415}
1416
1417static void reset()
1418{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001419 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001420}
1421
1422static void gracefulPowerOffTimerStart()
1423{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001424 lg2::info("Graceful power-off timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001425 gracefulPowerOffTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001426 std::chrono::seconds(TimerMap["GracefulPowerOffS"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001427 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1428 if (ec)
1429 {
1430 // operation_aborted is expected if timer is canceled before
1431 // completion.
1432 if (ec != boost::asio::error::operation_aborted)
1433 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001434 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}",
1435 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001436 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001437 lg2::info("Graceful power-off timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001438 return;
1439 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001440 lg2::info("Graceful power-off timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001441 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1442 });
1443}
1444
1445static void powerCycleTimerStart()
1446{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001447 lg2::info("Power-cycle timer started");
Priyatharshan P70120512020-09-16 18:47:20 +05301448 powerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001449 std::chrono::milliseconds(TimerMap["PowerCycleMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001450 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1451 if (ec)
1452 {
1453 // operation_aborted is expected if timer is canceled before
1454 // completion.
1455 if (ec != boost::asio::error::operation_aborted)
1456 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001457 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}",
1458 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001459 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001460 lg2::info("Power-cycle timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001461 return;
1462 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001463 lg2::info("Power-cycle timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001464 sendPowerControlEvent(Event::powerCycleTimerExpired);
1465 });
1466}
1467
Jason M. Bills35471132025-05-06 12:26:10 -07001468static void powerOKWatchdogTimerStart()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001469{
Jason M. Bills35471132025-05-06 12:26:10 -07001470 lg2::info("power OK watchdog timer started");
1471 powerOKWatchdogTimer.expires_after(
1472 std::chrono::milliseconds(TimerMap["PowerOKWatchdogMs"]));
1473 powerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) {
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001474 if (ec)
1475 {
1476 // operation_aborted is expected if timer is canceled before
1477 // completion.
1478 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001479 {
Jason M. Bills35471132025-05-06 12:26:10 -07001480 lg2::error("power OK watchdog async_wait failed: {ERROR_MSG}",
1481 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001482 }
Jason M. Bills35471132025-05-06 12:26:10 -07001483 lg2::info("power OK watchdog timer canceled");
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001484 return;
1485 }
Jason M. Bills35471132025-05-06 12:26:10 -07001486 lg2::info("power OK watchdog timer expired");
1487 sendPowerControlEvent(Event::powerOKWatchdogTimerExpired);
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001488 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001489}
1490
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001491static void warmResetCheckTimerStart()
1492{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001493 lg2::info("Warm reset check timer started");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001494 warmResetCheckTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001495 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"]));
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001496 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1497 if (ec)
1498 {
1499 // operation_aborted is expected if timer is canceled before
1500 // completion.
1501 if (ec != boost::asio::error::operation_aborted)
1502 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001503 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}",
1504 "ERROR_MSG", ec.message());
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001505 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001506 lg2::info("Warm reset check timer canceled");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001507 return;
1508 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001509 lg2::info("Warm reset check timer completed");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001510 sendPowerControlEvent(Event::warmResetDetected);
1511 });
1512}
1513
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001514static void pohCounterTimerStart()
1515{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001516 lg2::info("POH timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001517 // Set the time-out as 1 hour, to align with POH command in ipmid
1518 pohCounterTimer.expires_after(std::chrono::hours(1));
1519 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1520 if (ec)
1521 {
1522 // operation_aborted is expected if timer is canceled before
1523 // completion.
1524 if (ec != boost::asio::error::operation_aborted)
1525 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001526 lg2::error("POH timer async_wait failed: {ERROR_MSG}",
1527 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001528 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001529 lg2::info("POH timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001530 return;
1531 }
1532
1533 if (getHostState(powerState) !=
1534 "xyz.openbmc_project.State.Host.HostState.Running")
1535 {
1536 return;
1537 }
1538
1539 conn->async_method_call(
1540 [](boost::system::error_code ec,
1541 const std::variant<uint32_t>& pohCounterProperty) {
1542 if (ec)
1543 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001544 lg2::error("error getting poh counter");
1545 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001546 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001547 const uint32_t* pohCounter =
1548 std::get_if<uint32_t>(&pohCounterProperty);
1549 if (pohCounter == nullptr)
1550 {
1551 lg2::error("unable to read poh counter");
1552 return;
1553 }
1554
1555 conn->async_method_call(
1556 [](boost::system::error_code ec) {
1557 if (ec)
1558 {
1559 lg2::error("failed to set poh counter");
1560 }
1561 },
1562 "xyz.openbmc_project.Settings",
1563 "/xyz/openbmc_project/state/chassis0",
1564 "org.freedesktop.DBus.Properties", "Set",
1565 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1566 std::variant<uint32_t>(*pohCounter + 1));
Patrick Williamsd394c882023-10-20 11:18:44 -05001567 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001568 "xyz.openbmc_project.Settings",
1569 "/xyz/openbmc_project/state/chassis0",
1570 "org.freedesktop.DBus.Properties", "Get",
1571 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1572
1573 pohCounterTimerStart();
1574 });
1575}
1576
1577static void currentHostStateMonitor()
1578{
Yong Li8d660212019-12-27 10:18:10 +08001579 if (getHostState(powerState) ==
1580 "xyz.openbmc_project.State.Host.HostState.Running")
1581 {
1582 pohCounterTimerStart();
1583 // Clear the restart cause set for the next restart
1584 clearRestartCause();
1585 }
1586 else
1587 {
1588 pohCounterTimer.cancel();
1589 // Set the restart cause set for this restart
1590 setRestartCause();
1591 }
1592
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001593 static auto match = sdbusplus::bus::match_t(
1594 *conn,
1595 "type='signal',member='PropertiesChanged', "
1596 "interface='org.freedesktop.DBus.Properties', "
1597 "arg0='xyz.openbmc_project.State.Host'",
1598 [](sdbusplus::message_t& message) {
1599 std::string intfName;
1600 std::map<std::string, std::variant<std::string>> properties;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001601
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001602 try
1603 {
1604 message.read(intfName, properties);
1605 }
1606 catch (const std::exception& e)
1607 {
1608 lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1609 return;
1610 }
1611 if (properties.empty())
1612 {
1613 lg2::error("ERROR: Empty PropertiesChanged signal received");
1614 return;
1615 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001616
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001617 // We only want to check for CurrentHostState
1618 if (properties.begin()->first != "CurrentHostState")
1619 {
1620 return;
1621 }
1622 std::string* currentHostState =
1623 std::get_if<std::string>(&(properties.begin()->second));
1624 if (currentHostState == nullptr)
1625 {
1626 lg2::error("{PROPERTY} property invalid", "PROPERTY",
1627 properties.begin()->first);
1628 return;
1629 }
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001630
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001631 if (*currentHostState ==
1632 "xyz.openbmc_project.State.Host.HostState.Running")
1633 {
1634 pohCounterTimerStart();
1635 // Clear the restart cause set for the next restart
1636 clearRestartCause();
1637 sd_journal_send("MESSAGE=Host system DC power is on",
1638 "PRIORITY=%i", LOG_INFO,
1639 "REDFISH_MESSAGE_ID=%s",
1640 "OpenBMC.0.1.DCPowerOn", NULL);
1641 }
1642 else
1643 {
1644 pohCounterTimer.cancel();
1645 // POST_COMPLETE GPIO event is not working in some platforms
1646 // when power state is changed to OFF. This resulted in
1647 // 'OperatingSystemState' to stay at 'Standby', even though
1648 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1649 // if HostState is trurned to OFF.
1650 setOperatingSystemState(OperatingSystemStateStage::Inactive);
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301651
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001652 // Set the restart cause set for this restart
1653 setRestartCause();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001654#ifdef USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001655 resetACBootProperty();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001656#endif // USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001657 sd_journal_send("MESSAGE=Host system DC power is off",
1658 "PRIORITY=%i", LOG_INFO,
1659 "REDFISH_MESSAGE_ID=%s",
1660 "OpenBMC.0.1.DCPowerOff", NULL);
1661 }
1662 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001663}
1664
1665static void sioPowerGoodWatchdogTimerStart()
1666{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001667 lg2::info("SIO power good watchdog timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001668 sioPowerGoodWatchdogTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001669 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001670 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code
1671 ec) {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001672 if (ec)
1673 {
1674 // operation_aborted is expected if timer is canceled before
1675 // completion.
1676 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001677 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001678 lg2::error(
1679 "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1680 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001681 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001682 lg2::info("SIO power good watchdog timer canceled");
1683 return;
1684 }
1685 lg2::info("SIO power good watchdog timer completed");
1686 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1687 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001688}
1689
1690static void powerStateOn(const Event event)
1691{
1692 logEvent(__FUNCTION__, event);
1693 switch (event)
1694 {
Jason M. Bills35471132025-05-06 12:26:10 -07001695 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001696 setPowerState(PowerState::off);
1697 // DC power is unexpectedly lost, beep
1698 beep(beepPowerFail);
1699 break;
1700 case Event::sioS5Assert:
1701 setPowerState(PowerState::transitionToOff);
Matt Simmering58e379d2022-09-23 14:45:50 -07001702#if IGNORE_SOFT_RESETS_DURING_POST
1703 // Only recognize soft resets once host gets past POST COMPLETE
1704 if (operatingSystemState != OperatingSystemStateStage::Standby)
1705 {
1706 ignoreNextSoftReset = true;
1707 }
1708#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001709 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001710 break;
Jason M. Billsfb957332021-01-28 13:18:46 -08001711#if USE_PLT_RST
1712 case Event::pltRstAssert:
1713#else
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001714 case Event::postCompleteDeAssert:
Jason M. Billsfb957332021-01-28 13:18:46 -08001715#endif
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001716 setPowerState(PowerState::checkForWarmReset);
Matt Simmering58e379d2022-09-23 14:45:50 -07001717#if IGNORE_SOFT_RESETS_DURING_POST
1718 // Only recognize soft resets once host gets past POST COMPLETE
1719 if (operatingSystemState != OperatingSystemStateStage::Standby)
1720 {
1721 ignoreNextSoftReset = true;
1722 }
1723#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001724 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001725 warmResetCheckTimerStart();
1726 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001727 case Event::powerButtonPressed:
1728 setPowerState(PowerState::gracefulTransitionToOff);
1729 gracefulPowerOffTimerStart();
1730 break;
1731 case Event::powerOffRequest:
1732 setPowerState(PowerState::transitionToOff);
1733 forcePowerOff();
1734 break;
1735 case Event::gracefulPowerOffRequest:
1736 setPowerState(PowerState::gracefulTransitionToOff);
1737 gracefulPowerOffTimerStart();
1738 gracefulPowerOff();
1739 break;
1740 case Event::powerCycleRequest:
1741 setPowerState(PowerState::transitionToCycleOff);
1742 forcePowerOff();
1743 break;
1744 case Event::gracefulPowerCycleRequest:
1745 setPowerState(PowerState::gracefulTransitionToCycleOff);
1746 gracefulPowerOffTimerStart();
1747 gracefulPowerOff();
1748 break;
Jayanth Othayothdc0bab92024-02-07 07:24:35 -06001749 case Event::resetButtonPressed:
1750 setPowerState(PowerState::checkForWarmReset);
1751 warmResetCheckTimerStart();
1752 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001753 case Event::resetRequest:
1754 reset();
1755 break;
1756 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001757 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001758 break;
1759 }
1760}
1761
Jason M. Bills35471132025-05-06 12:26:10 -07001762static void powerStateWaitForPowerOK(const Event event)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001763{
1764 logEvent(__FUNCTION__, event);
1765 switch (event)
1766 {
Jason M. Bills35471132025-05-06 12:26:10 -07001767 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301768 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001769 // Cancel any GPIO assertions held during the transition
1770 gpioAssertTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001771 powerOKWatchdogTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301772 if (sioEnabled == true)
1773 {
1774 sioPowerGoodWatchdogTimerStart();
1775 setPowerState(PowerState::waitForSIOPowerGood);
1776 }
1777 else
1778 {
1779 setPowerState(PowerState::on);
1780 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001781 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301782 }
Jason M. Bills35471132025-05-06 12:26:10 -07001783 case Event::powerOKWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001784 setPowerState(PowerState::off);
Jason M. Bills35471132025-05-06 12:26:10 -07001785 powerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001786 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001787 case Event::sioPowerGoodAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001788 powerOKWatchdogTimer.cancel();
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001789 setPowerState(PowerState::on);
1790 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001791 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001792 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001793 break;
1794 }
1795}
1796
1797static void powerStateWaitForSIOPowerGood(const Event event)
1798{
1799 logEvent(__FUNCTION__, event);
1800 switch (event)
1801 {
1802 case Event::sioPowerGoodAssert:
1803 sioPowerGoodWatchdogTimer.cancel();
1804 setPowerState(PowerState::on);
1805 break;
1806 case Event::sioPowerGoodWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001807 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001808 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001809 break;
1810 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001811 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001812 break;
1813 }
1814}
1815
1816static void powerStateOff(const Event event)
1817{
1818 logEvent(__FUNCTION__, event);
1819 switch (event)
1820 {
Jason M. Bills35471132025-05-06 12:26:10 -07001821 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301822 {
1823 if (sioEnabled == true)
1824 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001825 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301826 setPowerState(PowerState::waitForSIOPowerGood);
1827 }
1828 else
1829 {
1830 setPowerState(PowerState::on);
1831 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001832 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301833 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001834 case Event::sioS5DeAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001835 powerOKWatchdogTimerStart();
1836 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001837 break;
Jason M. Bills273d7892020-06-17 14:46:57 -07001838 case Event::sioPowerGoodAssert:
1839 setPowerState(PowerState::on);
1840 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001841 case Event::powerButtonPressed:
Jason M. Bills35471132025-05-06 12:26:10 -07001842 powerOKWatchdogTimerStart();
1843 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001844 break;
1845 case Event::powerOnRequest:
Jason M. Bills35471132025-05-06 12:26:10 -07001846 powerOKWatchdogTimerStart();
1847 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001848 powerOn();
1849 break;
1850 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001851 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001852 break;
1853 }
1854}
1855
1856static void powerStateTransitionToOff(const Event event)
1857{
1858 logEvent(__FUNCTION__, event);
1859 switch (event)
1860 {
Jason M. Bills35471132025-05-06 12:26:10 -07001861 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001862 // Cancel any GPIO assertions held during the transition
1863 gpioAssertTimer.cancel();
1864 setPowerState(PowerState::off);
1865 break;
1866 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001867 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001868 break;
1869 }
1870}
1871
1872static void powerStateGracefulTransitionToOff(const Event event)
1873{
1874 logEvent(__FUNCTION__, event);
1875 switch (event)
1876 {
Jason M. Bills35471132025-05-06 12:26:10 -07001877 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001878 gracefulPowerOffTimer.cancel();
1879 setPowerState(PowerState::off);
1880 break;
1881 case Event::gracefulPowerOffTimerExpired:
1882 setPowerState(PowerState::on);
1883 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001884 case Event::powerOffRequest:
1885 gracefulPowerOffTimer.cancel();
1886 setPowerState(PowerState::transitionToOff);
1887 forcePowerOff();
1888 break;
1889 case Event::powerCycleRequest:
1890 gracefulPowerOffTimer.cancel();
1891 setPowerState(PowerState::transitionToCycleOff);
1892 forcePowerOff();
1893 break;
1894 case Event::resetRequest:
1895 gracefulPowerOffTimer.cancel();
1896 setPowerState(PowerState::on);
1897 reset();
1898 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001899 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001900 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001901 break;
1902 }
1903}
1904
1905static void powerStateCycleOff(const Event event)
1906{
1907 logEvent(__FUNCTION__, event);
1908 switch (event)
1909 {
Jason M. Bills35471132025-05-06 12:26:10 -07001910 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301911 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001912 powerCycleTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301913 if (sioEnabled == true)
1914 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001915 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301916 setPowerState(PowerState::waitForSIOPowerGood);
1917 }
1918 else
1919 {
1920 setPowerState(PowerState::on);
1921 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001922 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301923 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001924 case Event::sioS5DeAssert:
1925 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001926 powerOKWatchdogTimerStart();
1927 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001928 break;
1929 case Event::powerButtonPressed:
1930 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001931 powerOKWatchdogTimerStart();
1932 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001933 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001934 case Event::powerCycleTimerExpired:
Jason M. Bills35471132025-05-06 12:26:10 -07001935 powerOKWatchdogTimerStart();
1936 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001937 powerOn();
1938 break;
1939 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001940 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001941 break;
1942 }
1943}
1944
1945static void powerStateTransitionToCycleOff(const Event event)
1946{
1947 logEvent(__FUNCTION__, event);
1948 switch (event)
1949 {
Jason M. Bills35471132025-05-06 12:26:10 -07001950 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001951 // Cancel any GPIO assertions held during the transition
1952 gpioAssertTimer.cancel();
1953 setPowerState(PowerState::cycleOff);
1954 powerCycleTimerStart();
1955 break;
1956 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001957 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001958 break;
1959 }
1960}
1961
1962static void powerStateGracefulTransitionToCycleOff(const Event event)
1963{
1964 logEvent(__FUNCTION__, event);
1965 switch (event)
1966 {
Jason M. Bills35471132025-05-06 12:26:10 -07001967 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001968 gracefulPowerOffTimer.cancel();
1969 setPowerState(PowerState::cycleOff);
1970 powerCycleTimerStart();
1971 break;
1972 case Event::gracefulPowerOffTimerExpired:
1973 setPowerState(PowerState::on);
1974 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001975 case Event::powerOffRequest:
1976 gracefulPowerOffTimer.cancel();
1977 setPowerState(PowerState::transitionToOff);
1978 forcePowerOff();
1979 break;
1980 case Event::powerCycleRequest:
1981 gracefulPowerOffTimer.cancel();
1982 setPowerState(PowerState::transitionToCycleOff);
1983 forcePowerOff();
1984 break;
1985 case Event::resetRequest:
1986 gracefulPowerOffTimer.cancel();
1987 setPowerState(PowerState::on);
1988 reset();
1989 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001990 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001991 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001992 break;
1993 }
1994}
1995
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001996static void powerStateCheckForWarmReset(const Event event)
1997{
1998 logEvent(__FUNCTION__, event);
1999 switch (event)
2000 {
2001 case Event::sioS5Assert:
2002 warmResetCheckTimer.cancel();
2003 setPowerState(PowerState::transitionToOff);
2004 break;
2005 case Event::warmResetDetected:
2006 setPowerState(PowerState::on);
2007 break;
Jason M. Bills35471132025-05-06 12:26:10 -07002008 case Event::powerOKDeAssert:
P.K. Lee344dae82019-11-27 16:35:05 +08002009 warmResetCheckTimer.cancel();
2010 setPowerState(PowerState::off);
2011 // DC power is unexpectedly lost, beep
2012 beep(beepPowerFail);
2013 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002014 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002015 lg2::info("No action taken.");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002016 break;
2017 }
2018}
2019
Jason M. Bills35471132025-05-06 12:26:10 -07002020static void powerOKHandler(bool state)
Zev Weiss584aa132021-09-02 19:21:52 -05002021{
Zev Weissedc86f32024-05-07 01:44:33 +00002022 Event powerControlEvent = (state == powerOkConfig.polarity)
Jason M. Bills35471132025-05-06 12:26:10 -07002023 ? Event::powerOKAssert
2024 : Event::powerOKDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002025 sendPowerControlEvent(powerControlEvent);
2026}
2027
Zev Weiss584aa132021-09-02 19:21:52 -05002028static void sioPowerGoodHandler(bool state)
2029{
Zev Weissedc86f32024-05-07 01:44:33 +00002030 Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2031 ? Event::sioPowerGoodAssert
2032 : Event::sioPowerGoodDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002033 sendPowerControlEvent(powerControlEvent);
2034}
2035
Zev Weiss584aa132021-09-02 19:21:52 -05002036static void sioOnControlHandler(bool state)
2037{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002038 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2039 static_cast<int>(state));
Zev Weiss584aa132021-09-02 19:21:52 -05002040}
2041
Zev Weiss584aa132021-09-02 19:21:52 -05002042static void sioS5Handler(bool state)
2043{
Zev Weissedc86f32024-05-07 01:44:33 +00002044 Event powerControlEvent = (state == sioS5Config.polarity)
2045 ? Event::sioS5Assert
2046 : Event::sioS5DeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002047 sendPowerControlEvent(powerControlEvent);
2048}
2049
Zev Weiss584aa132021-09-02 19:21:52 -05002050static void powerButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002051{
Zev Weissedc86f32024-05-07 01:44:33 +00002052 bool asserted = state == powerButtonConfig.polarity;
2053 powerButtonIface->set_property("ButtonPressed", asserted);
2054 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002055 {
2056 powerButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002057 if (!powerButtonMask)
2058 {
2059 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002060 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002061 }
2062 else
2063 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002064 lg2::info("power button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002065 }
2066 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002067#if USE_BUTTON_PASSTHROUGH
2068 gpiod::line gpioLine;
2069 bool outputState =
2070 asserted ? powerOutConfig.polarity : (!powerOutConfig.polarity);
2071 if (!setGPIOOutput(powerOutConfig.lineName, outputState, gpioLine))
2072 {
2073 lg2::error("{GPIO_NAME} power button passthrough failed", "GPIO_NAME",
2074 powerOutConfig.lineName);
2075 }
2076#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002077}
2078
Zev Weiss584aa132021-09-02 19:21:52 -05002079static void resetButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002080{
Zev Weissedc86f32024-05-07 01:44:33 +00002081 bool asserted = state == resetButtonConfig.polarity;
2082 resetButtonIface->set_property("ButtonPressed", asserted);
2083 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002084 {
2085 resetButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002086 if (!resetButtonMask)
2087 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002088 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002089 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002090 }
2091 else
2092 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002093 lg2::info("reset button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002094 }
2095 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002096#if USE_BUTTON_PASSTHROUGH
2097 gpiod::line gpioLine;
2098 bool outputState =
2099 asserted ? resetOutConfig.polarity : (!resetOutConfig.polarity);
2100 if (!setGPIOOutput(resetOutConfig.lineName, outputState, gpioLine))
2101 {
2102 lg2::error("{GPIO_NAME} reset button passthrough failed", "GPIO_NAME",
2103 resetOutConfig.lineName);
2104 }
2105#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002106}
2107
Vijay Khemka04175c22020-10-09 14:28:11 -07002108#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002109static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2110static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2111static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2112static constexpr auto systemTargetName = "chassis-system-reset.target";
2113
2114void systemReset()
2115{
2116 conn->async_method_call(
2117 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002118 if (ec)
2119 {
2120 lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2121 ec.message());
2122 }
2123 },
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002124 systemdBusname, systemdPath, systemdInterface, "StartUnit",
2125 systemTargetName, "replace");
2126}
Vijay Khemka04175c22020-10-09 14:28:11 -07002127#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002128
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002129static void nmiSetEnableProperty(bool value)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002130{
2131 conn->async_method_call(
2132 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002133 if (ec)
2134 {
2135 lg2::error("failed to set NMI source");
2136 }
2137 },
Chen Yugang303bd582019-11-01 08:45:06 +08002138 "xyz.openbmc_project.Settings",
2139 "/xyz/openbmc_project/Chassis/Control/NMISource",
2140 "org.freedesktop.DBus.Properties", "Set",
2141 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2142 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002143}
2144
2145static void nmiReset(void)
2146{
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002147 const static constexpr int nmiOutPulseTimeMs = 200;
2148
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002149 lg2::info("NMI out action");
Jian Zhang461a1662022-09-22 11:29:01 +08002150 nmiOutLine.set_value(nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002151 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
Jian Zhang461a1662022-09-22 11:29:01 +08002152 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002153 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2154 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2155 // restore the NMI_OUT GPIO line back to the opposite value
Jian Zhang461a1662022-09-22 11:29:01 +08002156 nmiOutLine.set_value(!nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002157 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002158 if (ec)
2159 {
2160 // operation_aborted is expected if timer is canceled before
2161 // completion.
2162 if (ec != boost::asio::error::operation_aborted)
2163 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002164 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2165 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2166 ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002167 }
2168 }
2169 });
2170 // log to redfish
2171 nmiDiagIntLog();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002172 lg2::info("NMI out action completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002173 // reset Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002174 nmiSetEnableProperty(false);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002175}
2176
2177static void nmiSourcePropertyMonitor(void)
2178{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002179 lg2::info("NMI Source Property Monitor");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002180
Patrick Williams439b9c32022-07-22 19:26:53 -05002181 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2182 std::make_unique<sdbusplus::bus::match_t>(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002183 *conn,
2184 "type='signal',interface='org.freedesktop.DBus.Properties',"
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002185 "member='PropertiesChanged',"
2186 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
Patrick Williams439b9c32022-07-22 19:26:53 -05002187 [](sdbusplus::message_t& msg) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002188 std::string interfaceName;
2189 boost::container::flat_map<std::string,
2190 std::variant<bool, std::string>>
2191 propertiesChanged;
2192 std::string state;
2193 bool value = true;
2194 try
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002195 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002196 msg.read(interfaceName, propertiesChanged);
2197 if (propertiesChanged.begin()->first == "Enabled")
2198 {
2199 value =
2200 std::get<bool>(propertiesChanged.begin()->second);
2201 lg2::info(
2202 "NMI Enabled propertiesChanged value: {VALUE}",
2203 "VALUE", value);
2204 nmiEnabled = value;
2205 if (nmiEnabled)
2206 {
2207 nmiReset();
2208 }
2209 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002210 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002211 catch (const std::exception& e)
2212 {
2213 lg2::error("Unable to read NMI source: {ERROR}", "ERROR",
2214 e);
2215 return;
2216 }
2217 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002218}
2219
2220static void setNmiSource()
2221{
2222 conn->async_method_call(
2223 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002224 if (ec)
2225 {
2226 lg2::error("failed to set NMI source");
2227 }
2228 },
Chen Yugang303bd582019-11-01 08:45:06 +08002229 "xyz.openbmc_project.Settings",
2230 "/xyz/openbmc_project/Chassis/Control/NMISource",
2231 "org.freedesktop.DBus.Properties", "Set",
2232 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
Jason M. Bills418ce112021-09-08 15:15:05 -07002233 std::variant<std::string>{
Tim Lee6af569f2024-03-11 17:32:42 +08002234 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002235 // set Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002236 nmiSetEnableProperty(true);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002237}
2238
Zev Weiss584aa132021-09-02 19:21:52 -05002239static void nmiButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002240{
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002241 // Don't handle event if host not running and config doesn't force it
2242 if (!nmiWhenPoweredOff &&
2243 getHostState(powerState) !=
2244 "xyz.openbmc_project.State.Host.HostState.Running")
2245 {
2246 return;
2247 }
Zev Weissedc86f32024-05-07 01:44:33 +00002248
2249 bool asserted = state == nmiButtonConfig.polarity;
2250 nmiButtonIface->set_property("ButtonPressed", asserted);
2251 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002252 {
2253 nmiButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002254 if (nmiButtonMasked)
2255 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002256 lg2::info("NMI button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002257 }
2258 else
2259 {
2260 setNmiSource();
2261 }
2262 }
Zev Weiss584aa132021-09-02 19:21:52 -05002263}
2264
Zev Weiss584aa132021-09-02 19:21:52 -05002265static void idButtonHandler(bool state)
2266{
Zev Weissedc86f32024-05-07 01:44:33 +00002267 bool asserted = state == idButtonConfig.polarity;
2268 idButtonIface->set_property("ButtonPressed", asserted);
Zev Weiss584aa132021-09-02 19:21:52 -05002269}
2270
Jason M. Billsfb957332021-01-28 13:18:46 -08002271static void pltRstHandler(bool pltRst)
2272{
2273 if (pltRst)
2274 {
2275 sendPowerControlEvent(Event::pltRstDeAssert);
2276 }
2277 else
2278 {
2279 sendPowerControlEvent(Event::pltRstAssert);
2280 }
2281}
2282
Patrick Williams439b9c32022-07-22 19:26:53 -05002283[[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
Jason M. Billsfb957332021-01-28 13:18:46 -08002284{
2285 std::string interfaceName;
2286 boost::container::flat_map<std::string, std::variant<bool>>
2287 propertiesChanged;
Jason M. Billsfb957332021-01-28 13:18:46 -08002288 try
2289 {
2290 msg.read(interfaceName, propertiesChanged);
2291 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002292 catch (const std::exception& e)
Jason M. Billsfb957332021-01-28 13:18:46 -08002293 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002294 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
Jason M. Billsfb957332021-01-28 13:18:46 -08002295 return;
2296 }
2297 if (propertiesChanged.empty())
2298 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002299 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
Jason M. Billsfb957332021-01-28 13:18:46 -08002300 return;
2301 }
2302
2303 for (auto& [property, value] : propertiesChanged)
2304 {
2305 if (property == "ESpiPlatformReset")
2306 {
2307 bool* pltRst = std::get_if<bool>(&value);
2308 if (pltRst == nullptr)
2309 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002310 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
Jason M. Billsfb957332021-01-28 13:18:46 -08002311 return;
2312 }
2313 pltRstHandler(*pltRst);
2314 }
2315 }
2316}
2317
Zev Weiss584aa132021-09-02 19:21:52 -05002318static void postCompleteHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002319{
Zev Weissedc86f32024-05-07 01:44:33 +00002320 bool asserted = state == postCompleteConfig.polarity;
2321 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002322 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002323 sendPowerControlEvent(Event::postCompleteAssert);
Tim Lee86239182021-12-23 11:46:01 +08002324 setOperatingSystemState(OperatingSystemStateStage::Standby);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002325 }
2326 else
2327 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002328 sendPowerControlEvent(Event::postCompleteDeAssert);
Tim Lee86239182021-12-23 11:46:01 +08002329 setOperatingSystemState(OperatingSystemStateStage::Inactive);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002330 }
Zev Weiss584aa132021-09-02 19:21:52 -05002331}
2332
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302333static int loadConfigValues()
2334{
2335 const std::string configFilePath =
2336 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2337 ".json";
2338 std::ifstream configFile(configFilePath.c_str());
2339 if (!configFile.is_open())
2340 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002341 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2342 "PATH", configFilePath);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302343 return -1;
2344 }
Zev Weiss1aa08b22021-09-15 17:06:20 -05002345 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302346
Priyatharshan P70120512020-09-16 18:47:20 +05302347 if (jsonData.is_discarded())
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302348 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002349 lg2::error("Power config readings JSON parser failure");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302350 return -1;
2351 }
Priyatharshan P70120512020-09-16 18:47:20 +05302352 auto gpios = jsonData["gpio_configs"];
2353 auto timers = jsonData["timing_configs"];
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302354
Priyatharshan P70120512020-09-16 18:47:20 +05302355 ConfigData* tempGpioData;
2356
2357 for (nlohmann::json& gpioConfig : gpios)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302358 {
Priyatharshan P70120512020-09-16 18:47:20 +05302359 if (!gpioConfig.contains("Name"))
2360 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002361 lg2::error("The 'Name' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302362 return -1;
2363 }
2364
2365 // Iterate through the powersignal map to check if the gpio json config
2366 // entry is valid
2367 std::string gpioName = gpioConfig["Name"];
2368 auto signalMapIter = powerSignalMap.find(gpioName);
2369 if (signalMapIter == powerSignalMap.end())
2370 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002371 lg2::error(
2372 "{GPIO_NAME} is not a recognized power-control signal name",
2373 "GPIO_NAME", gpioName);
Priyatharshan P70120512020-09-16 18:47:20 +05302374 return -1;
2375 }
2376
2377 // assign the power signal name to the corresponding structure reference
2378 // from map then fillup the structure with coressponding json config
2379 // value
2380 tempGpioData = signalMapIter->second;
2381 tempGpioData->name = gpioName;
2382
2383 if (!gpioConfig.contains("Type"))
2384 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002385 lg2::error("The \'Type\' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302386 return -1;
2387 }
2388
2389 std::string signalType = gpioConfig["Type"];
2390 if (signalType == "GPIO")
2391 {
2392 tempGpioData->type = ConfigType::GPIO;
2393 }
2394 else if (signalType == "DBUS")
2395 {
2396 tempGpioData->type = ConfigType::DBUS;
2397 }
2398 else
2399 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002400 lg2::error("{TYPE} is not a recognized power-control signal type",
2401 "TYPE", signalType);
Priyatharshan P70120512020-09-16 18:47:20 +05302402 return -1;
2403 }
2404
2405 if (tempGpioData->type == ConfigType::GPIO)
2406 {
2407 if (gpioConfig.contains("LineName"))
2408 {
2409 tempGpioData->lineName = gpioConfig["LineName"];
2410 }
2411 else
2412 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002413 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002414 "The \'LineName\' field must be defined for GPIO configuration");
Priyatharshan P70120512020-09-16 18:47:20 +05302415 return -1;
2416 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002417 if (gpioConfig.contains("Polarity"))
2418 {
2419 std::string polarity = gpioConfig["Polarity"];
2420 if (polarity == "ActiveLow")
2421 {
2422 tempGpioData->polarity = false;
2423 }
2424 else if (polarity == "ActiveHigh")
2425 {
2426 tempGpioData->polarity = true;
2427 }
2428 else
2429 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002430 lg2::error(
2431 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2432 "POLARITY", polarity);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002433 return -1;
2434 }
2435 }
2436 else
2437 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002438 lg2::error("Polarity field not found for {GPIO_NAME}",
2439 "GPIO_NAME", tempGpioData->lineName);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002440 return -1;
2441 }
Priyatharshan P70120512020-09-16 18:47:20 +05302442 }
2443 else
2444 {
2445 // if dbus based gpio config is defined read and update the dbus
2446 // params corresponding to the gpio config instance
2447 for (auto& [key, dbusParamName] : dbusParams)
2448 {
Logananth Sundararaja4308042021-10-20 11:52:05 +05302449 if (!gpioConfig.contains(dbusParamName))
Priyatharshan P70120512020-09-16 18:47:20 +05302450 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002451 lg2::error(
2452 "The {DBUS_NAME} field must be defined for Dbus configuration ",
2453 "DBUS_NAME", dbusParamName);
Priyatharshan P70120512020-09-16 18:47:20 +05302454 return -1;
2455 }
2456 }
Logananth Sundararaja4308042021-10-20 11:52:05 +05302457 tempGpioData->dbusName =
2458 gpioConfig[dbusParams[DbusConfigType::name]];
2459 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
Priyatharshan P70120512020-09-16 18:47:20 +05302460 tempGpioData->interface =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302461 gpioConfig[dbusParams[DbusConfigType::interface]];
Priyatharshan P70120512020-09-16 18:47:20 +05302462 tempGpioData->lineName =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302463 gpioConfig[dbusParams[DbusConfigType::property]];
Zev Weissca478552024-06-11 23:45:58 +00002464
Zev Weissd603dd12024-06-19 21:50:52 +00002465 // dbus-based inputs must be active-high.
2466 tempGpioData->polarity = true;
2467
Zev Weissca478552024-06-11 23:45:58 +00002468 // MatchRegex is optional
2469 auto item = gpioConfig.find("MatchRegex");
2470 if (item != gpioConfig.end())
2471 {
2472 try
2473 {
2474 tempGpioData->matchRegex = std::regex(*item);
2475 }
2476 catch (const std::regex_error& e)
2477 {
2478 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2479 gpioName, "ERR", e.what());
2480 return -1;
2481 }
2482 }
Priyatharshan P70120512020-09-16 18:47:20 +05302483 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302484 }
2485
Priyatharshan P70120512020-09-16 18:47:20 +05302486 // read and store the timer values from json config to Timer Map
2487 for (auto& [key, timerValue] : TimerMap)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302488 {
Priyatharshan P70120512020-09-16 18:47:20 +05302489 if (timers.contains(key.c_str()))
2490 {
2491 timerValue = timers[key.c_str()];
2492 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302493 }
2494
Jonathan Doman891bbde2023-05-17 13:58:24 -07002495 // If "events_configs" key is not in json config, fallback to null
2496 auto events = jsonData.value("event_configs",
2497 nlohmann::json(nlohmann::json::value_t::null));
2498 if (events.is_object())
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002499 {
2500 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2501 }
2502
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302503 return 0;
2504}
Zev Weissa8f116a2021-09-01 21:08:30 -05002505
Zev Weissca478552024-06-11 23:45:58 +00002506template <typename T>
Patrick Williamsb84e7892025-02-01 08:22:06 -05002507static std::optional<T> getMessageValue(sdbusplus::message_t& msg,
2508 const std::string& name)
Zev Weissa8f116a2021-09-01 21:08:30 -05002509{
Zev Weissa8f116a2021-09-01 21:08:30 -05002510 std::string event;
Zev Weissca478552024-06-11 23:45:58 +00002511 std::string thresholdInterface;
2512 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2513
2514 msg.read(thresholdInterface, propertiesChanged);
2515 if (propertiesChanged.empty())
2516 {
2517 return std::nullopt;
2518 }
2519
2520 event = propertiesChanged.begin()->first;
2521 if (event.empty() || event != name)
2522 {
2523 return std::nullopt;
2524 }
2525
2526 return std::get<T>(propertiesChanged.begin()->second);
2527}
2528
2529static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2530 const ConfigData& config, bool& value)
2531{
Zev Weissa8f116a2021-09-01 21:08:30 -05002532 try
2533 {
Zev Weissca478552024-06-11 23:45:58 +00002534 if (config.matchRegex.has_value())
Zev Weissa8f116a2021-09-01 21:08:30 -05002535 {
Zev Weissca478552024-06-11 23:45:58 +00002536 std::optional<std::string> s =
2537 getMessageValue<std::string>(msg, config.lineName);
2538 if (!s.has_value())
2539 {
2540 return false;
2541 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002542
Zev Weissca478552024-06-11 23:45:58 +00002543 std::smatch m;
2544 value = std::regex_match(s.value(), m, config.matchRegex.value());
2545 }
2546 else
Zev Weissa8f116a2021-09-01 21:08:30 -05002547 {
Zev Weissca478552024-06-11 23:45:58 +00002548 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2549 if (!v.has_value())
2550 {
2551 return false;
2552 }
2553 value = v.value();
Zev Weissa8f116a2021-09-01 21:08:30 -05002554 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002555 return true;
2556 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002557 catch (const std::exception& e)
Zev Weissa8f116a2021-09-01 21:08:30 -05002558 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002559 lg2::error(
2560 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
Zev Weissca478552024-06-11 23:45:58 +00002561 "DBUS_NAME", config.lineName, "ERROR", e);
Zev Weissa8f116a2021-09-01 21:08:30 -05002562 return false;
2563 }
2564}
2565
Patrick Williamsb84e7892025-02-01 08:22:06 -05002566static sdbusplus::bus::match_t dbusGPIOMatcher(
2567 const ConfigData& cfg, std::function<void(bool)> onMatch)
Zev Weissa8f116a2021-09-01 21:08:30 -05002568{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002569 auto pulseEventMatcherCallback =
2570 [&cfg, onMatch](sdbusplus::message_t& msg) {
2571 bool value = false;
2572 if (!getDbusMsgGPIOState(msg, cfg, value))
2573 {
2574 return;
2575 }
2576 onMatch(value);
2577 };
Zev Weissa8f116a2021-09-01 21:08:30 -05002578
Patrick Williams439b9c32022-07-22 19:26:53 -05002579 return sdbusplus::bus::match_t(
2580 static_cast<sdbusplus::bus_t&>(*conn),
Zev Weissa8f116a2021-09-01 21:08:30 -05002581 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2582 "PropertiesChanged',arg0='" +
Zev Weiss1cc79212024-06-19 22:14:57 +00002583 cfg.interface + "',path='" + cfg.path + "',sender='" +
2584 cfg.dbusName + "'",
Zev Weissa8f116a2021-09-01 21:08:30 -05002585 std::move(pulseEventMatcherCallback));
2586}
2587
Michal Orzeledd211e2022-10-28 13:10:16 +02002588// D-Bus property read functions
2589void reschedulePropertyRead(const ConfigData& configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302590
Michal Orzeledd211e2022-10-28 13:10:16 +02002591int getProperty(const ConfigData& configData)
2592{
2593 std::variant<bool> resp;
2594
2595 try
Priyatharshan P70120512020-09-16 18:47:20 +05302596 {
Michal Orzeledd211e2022-10-28 13:10:16 +02002597 auto method = conn->new_method_call(
2598 configData.dbusName.c_str(), configData.path.c_str(),
2599 "org.freedesktop.DBus.Properties", "Get");
2600 method.append(configData.interface.c_str(),
2601 configData.lineName.c_str());
2602
2603 auto reply = conn->call(method);
2604 if (reply.is_method_error())
2605 {
2606 lg2::error(
2607 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
2608 "PROPERTY", configData.lineName, "INTERFACE",
2609 configData.interface, "PATH", configData.path);
2610 return -1;
2611 }
2612
2613 reply.read(resp);
2614 }
2615 catch (const sdbusplus::exception_t& e)
2616 {
2617 lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
2618 configData.lineName, "WHAT", e.what());
2619 reschedulePropertyRead(configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302620 return -1;
2621 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002622
Logananth Sundararaj85e111e2021-11-11 13:13:13 +05302623 auto respValue = std::get_if<bool>(&resp);
Priyatharshan P70120512020-09-16 18:47:20 +05302624 if (!respValue)
2625 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002626 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2627 "PROPERTY", configData.lineName);
Priyatharshan P70120512020-09-16 18:47:20 +05302628 return -1;
2629 }
2630 return (*respValue);
2631}
Michal Orzeledd211e2022-10-28 13:10:16 +02002632
2633void setInitialValue(const ConfigData& configData, bool initialValue)
2634{
2635 if (configData.name == "PowerOk")
2636 {
2637 powerState = (initialValue ? PowerState::on : PowerState::off);
2638 hostIface->set_property("CurrentHostState",
2639 std::string(getHostState(powerState)));
2640 }
2641 else if (configData.name == "PowerButton")
2642 {
2643 powerButtonIface->set_property("ButtonPressed", !initialValue);
2644 }
2645 else if (configData.name == "ResetButton")
2646 {
2647 resetButtonIface->set_property("ButtonPressed", !initialValue);
2648 }
2649 else if (configData.name == "NMIButton")
2650 {
2651 nmiButtonIface->set_property("ButtonPressed", !initialValue);
2652 }
2653 else if (configData.name == "IdButton")
2654 {
2655 idButtonIface->set_property("ButtonPressed", !initialValue);
2656 }
2657 else if (configData.name == "PostComplete")
2658 {
2659 OperatingSystemStateStage osState =
Jason M. Billsc6d75652024-09-10 16:54:26 -07002660 (initialValue == postCompleteConfig.polarity
2661 ? OperatingSystemStateStage::Standby
2662 : OperatingSystemStateStage::Inactive);
Michal Orzeledd211e2022-10-28 13:10:16 +02002663 setOperatingSystemState(osState);
2664 }
2665 else
2666 {
2667 lg2::error("Unknown name {NAME}", "NAME", configData.name);
2668 }
2669}
2670
2671void reschedulePropertyRead(const ConfigData& configData)
2672{
2673 auto item = dBusRetryTimers.find(configData.name);
2674
2675 if (item == dBusRetryTimers.end())
2676 {
2677 auto newItem = dBusRetryTimers.insert(
2678 {configData.name, boost::asio::steady_timer(io)});
2679
2680 if (!newItem.second)
2681 {
2682 lg2::error("Failed to add new timer for {NAME}", "NAME",
2683 configData.name);
2684 return;
2685 }
2686
2687 item = newItem.first;
2688 }
2689
2690 auto& timer = item->second;
2691 timer.expires_after(
2692 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2693 timer.async_wait([&configData](const boost::system::error_code ec) {
2694 if (ec)
2695 {
2696 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2697 configData.name, "MSG", ec.message());
2698 dBusRetryTimers.erase(configData.name);
2699 return;
2700 }
2701
2702 int property = getProperty(configData);
2703
2704 if (property >= 0)
2705 {
2706 setInitialValue(configData, (property > 0));
2707 dBusRetryTimers.erase(configData.name);
2708 }
2709 });
2710}
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002711} // namespace power_control
2712
2713int main(int argc, char* argv[])
2714{
Lei YU92caa4c2021-02-23 16:59:25 +08002715 using namespace power_control;
Priyatharshan P70120512020-09-16 18:47:20 +05302716
2717 if (argc > 1)
2718 {
2719 node = argv[1];
2720 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002721 lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2722 node);
Priyatharshan P70120512020-09-16 18:47:20 +05302723
Lei YU92caa4c2021-02-23 16:59:25 +08002724 conn = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002725
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302726 // Load GPIO's through json config file
Lei YU92caa4c2021-02-23 16:59:25 +08002727 if (loadConfigValues() == -1)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302728 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002729 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302730 }
Naveen Mosesec972d82021-07-16 21:19:23 +05302731 /* Currently for single host based systems additional busname is added
2732 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2733 Going forward for single hosts the old bus name without zero numbering
2734 will be removed when all other applications adapted to the
2735 bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2736
2737 if (node == "0")
2738 {
2739 // Request all the dbus names
2740 conn->request_name(hostDbusName.c_str());
2741 conn->request_name(chassisDbusName.c_str());
2742 conn->request_name(osDbusName.c_str());
2743 conn->request_name(buttonDbusName.c_str());
2744 conn->request_name(nmiDbusName.c_str());
2745 conn->request_name(rstCauseDbusName.c_str());
2746 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302747
Zev Weissc4005bd2021-09-01 22:30:23 -05002748 hostDbusName += node;
2749 chassisDbusName += node;
2750 osDbusName += node;
2751 buttonDbusName += node;
2752 nmiDbusName += node;
2753 rstCauseDbusName += node;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002754
Priyatharshan P70120512020-09-16 18:47:20 +05302755 // Request all the dbus names
2756 conn->request_name(hostDbusName.c_str());
2757 conn->request_name(chassisDbusName.c_str());
2758 conn->request_name(osDbusName.c_str());
2759 conn->request_name(buttonDbusName.c_str());
2760 conn->request_name(nmiDbusName.c_str());
2761 conn->request_name(rstCauseDbusName.c_str());
2762
2763 if (sioPwrGoodConfig.lineName.empty() ||
2764 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
Priyatharshan P19c47a32020-08-12 18:16:43 +05302765 {
Lei YU92caa4c2021-02-23 16:59:25 +08002766 sioEnabled = false;
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002767 lg2::info("SIO control GPIOs not defined, disable SIO support.");
Priyatharshan P19c47a32020-08-12 18:16:43 +05302768 }
2769
Jason M. Bills35471132025-05-06 12:26:10 -07002770 // Request power OK GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302771 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002772 {
Jason M. Bills35471132025-05-06 12:26:10 -07002773 if (!requestGPIOEvents(powerOkConfig.lineName, powerOKHandler,
2774 powerOKLine, powerOKEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302775 {
2776 return -1;
2777 }
2778 }
Priyatharshan P70120512020-09-16 18:47:20 +05302779 else if (powerOkConfig.type == ConfigType::DBUS)
2780 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002781 static sdbusplus::bus::match_t powerOkEventMonitor =
Jason M. Bills35471132025-05-06 12:26:10 -07002782 power_control::dbusGPIOMatcher(powerOkConfig, powerOKHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302783 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302784 else
2785 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002786 lg2::error("PowerOk name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002787 return -1;
2788 }
2789
Lei YU92caa4c2021-02-23 16:59:25 +08002790 if (sioEnabled == true)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002791 {
Priyatharshan P19c47a32020-08-12 18:16:43 +05302792 // Request SIO_POWER_GOOD GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302793 if (sioPwrGoodConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302794 {
Priyatharshan P70120512020-09-16 18:47:20 +05302795 if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002796 sioPowerGoodHandler, sioPowerGoodLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302797 sioPowerGoodEvent))
2798 {
2799 return -1;
2800 }
2801 }
2802 else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2803 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002804 static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002805 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2806 sioPowerGoodHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302807 }
2808 else
2809 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002810 lg2::error(
Priyatharshan P70120512020-09-16 18:47:20 +05302811 "sioPwrGood name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302812 return -1;
2813 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002814
Priyatharshan P19c47a32020-08-12 18:16:43 +05302815 // Request SIO_ONCONTROL GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302816 if (sioOnControlConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302817 {
Priyatharshan P70120512020-09-16 18:47:20 +05302818 if (!requestGPIOEvents(sioOnControlConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002819 sioOnControlHandler, sioOnControlLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302820 sioOnControlEvent))
2821 {
2822 return -1;
2823 }
2824 }
2825 else if (sioOnControlConfig.type == ConfigType::DBUS)
2826 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002827 static sdbusplus::bus::match_t sioOnControlEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002828 power_control::dbusGPIOMatcher(sioOnControlConfig,
2829 sioOnControlHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302830 }
2831 else
2832 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002833 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002834 "sioOnControl name should be configured from jsonconfig file\n");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302835 return -1;
2836 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002837
Priyatharshan P19c47a32020-08-12 18:16:43 +05302838 // Request SIO_S5 GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302839 if (sioS5Config.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302840 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002841 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
Priyatharshan P70120512020-09-16 18:47:20 +05302842 sioS5Line, sioS5Event))
2843 {
2844 return -1;
2845 }
2846 }
2847 else if (sioS5Config.type == ConfigType::DBUS)
2848 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002849 static sdbusplus::bus::match_t sioS5EventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002850 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
Priyatharshan P70120512020-09-16 18:47:20 +05302851 }
2852 else
2853 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002854 lg2::error("sioS5 name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302855 return -1;
2856 }
2857 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002858
2859 // Request POWER_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302860 if (powerButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002861 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002862 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2863 powerButtonLine, powerButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302864 {
2865 return -1;
2866 }
2867 }
Priyatharshan P70120512020-09-16 18:47:20 +05302868 else if (powerButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302869 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002870 static sdbusplus::bus::match_t powerButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002871 power_control::dbusGPIOMatcher(powerButtonConfig,
2872 powerButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002873 }
2874
2875 // Request RESET_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302876 if (resetButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002877 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002878 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2879 resetButtonLine, resetButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302880 {
2881 return -1;
2882 }
2883 }
Priyatharshan P70120512020-09-16 18:47:20 +05302884 else if (resetButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302885 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002886 static sdbusplus::bus::match_t resetButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002887 power_control::dbusGPIOMatcher(resetButtonConfig,
2888 resetButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002889 }
2890
2891 // Request NMI_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302892 if (nmiButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302893 {
Priyatharshan P70120512020-09-16 18:47:20 +05302894 if (!nmiButtonConfig.lineName.empty())
2895 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002896 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302897 nmiButtonLine, nmiButtonEvent);
2898 }
2899 }
2900 else if (nmiButtonConfig.type == ConfigType::DBUS)
2901 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002902 static sdbusplus::bus::match_t nmiButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002903 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302904 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002905
2906 // Request ID_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302907 if (idButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302908 {
Priyatharshan P70120512020-09-16 18:47:20 +05302909 if (!idButtonConfig.lineName.empty())
2910 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002911 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302912 idButtonLine, idButtonEvent);
2913 }
2914 }
2915 else if (idButtonConfig.type == ConfigType::DBUS)
2916 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002917 static sdbusplus::bus::match_t idButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002918 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302919 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002920
Jason M. Billsfb957332021-01-28 13:18:46 -08002921#ifdef USE_PLT_RST
Patrick Williams439b9c32022-07-22 19:26:53 -05002922 sdbusplus::bus::match_t pltRstMatch(
Lei YU92caa4c2021-02-23 16:59:25 +08002923 *conn,
Jason M. Billsfb957332021-01-28 13:18:46 -08002924 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2925 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
Lei YU92caa4c2021-02-23 16:59:25 +08002926 hostMiscHandler);
Jason M. Billsfb957332021-01-28 13:18:46 -08002927#endif
2928
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002929 // Request POST_COMPLETE GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302930 if (postCompleteConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002931 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002932 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2933 postCompleteLine, postCompleteEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302934 {
2935 return -1;
2936 }
2937 }
Priyatharshan P70120512020-09-16 18:47:20 +05302938 else if (postCompleteConfig.type == ConfigType::DBUS)
2939 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002940 static sdbusplus::bus::match_t postCompleteEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002941 power_control::dbusGPIOMatcher(postCompleteConfig,
2942 postCompleteHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302943 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302944 else
2945 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002946 lg2::error(
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002947 "postComplete name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002948 return -1;
2949 }
2950
2951 // initialize NMI_OUT GPIO.
Priyatharshan P70120512020-09-16 18:47:20 +05302952 if (!nmiOutConfig.lineName.empty())
2953 {
Jian Zhang461a1662022-09-22 11:29:01 +08002954 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2955 nmiOutLine);
Priyatharshan P70120512020-09-16 18:47:20 +05302956 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002957
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002958 // Initialize POWER_OUT and RESET_OUT GPIO.
2959 gpiod::line line;
Priyatharshan P70120512020-09-16 18:47:20 +05302960 if (!powerOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002961 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002962 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2963 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302964 {
2965 return -1;
2966 }
2967 }
2968 else
2969 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002970 lg2::error("powerOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002971 return -1;
2972 }
2973
Priyatharshan P70120512020-09-16 18:47:20 +05302974 if (!resetOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002975 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002976 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2977 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302978 {
2979 return -1;
2980 }
2981 }
2982 else
2983 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002984 lg2::error("ResetOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002985 return -1;
2986 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002987 // Release line
2988 line.reset();
2989
Matt Simmering58e379d2022-09-23 14:45:50 -07002990 // Initialize the power state and operating system state
Lei YU92caa4c2021-02-23 16:59:25 +08002991 powerState = PowerState::off;
Matt Simmering58e379d2022-09-23 14:45:50 -07002992 operatingSystemState = OperatingSystemStateStage::Inactive;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002993 // Check power good
Priyatharshan P70120512020-09-16 18:47:20 +05302994
2995 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002996 {
Jason M. Bills35471132025-05-06 12:26:10 -07002997 if (powerOKLine.get_value() > 0 ||
Lei YUa37c2472021-09-26 15:57:12 +08002998 (sioEnabled &&
2999 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
Priyatharshan P70120512020-09-16 18:47:20 +05303000 {
3001 powerState = PowerState::on;
3002 }
3003 }
3004 else
3005 {
3006 if (getProperty(powerOkConfig))
3007 {
3008 powerState = PowerState::on;
3009 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003010 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003011 // Check if we need to start the Power Restore policy
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03003012 if (powerState != PowerState::on)
3013 {
3014 powerRestore.run();
3015 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003016
Lei YU92caa4c2021-02-23 16:59:25 +08003017 if (nmiOutLine)
3018 nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003019
Jason M. Billsc46ebb42021-11-10 11:41:31 -08003020 lg2::info("Initializing power state.");
Lei YU92caa4c2021-02-23 16:59:25 +08003021 logStateTransition(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003022
3023 // Power Control Service
3024 sdbusplus::asio::object_server hostServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003025 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003026
3027 // Power Control Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303028 hostIface =
3029 hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
3030 "xyz.openbmc_project.State.Host");
Vernon Maueryb4d03b12021-05-26 19:11:41 -07003031 // Interface for IPMI/Redfish initiated host state transitions
Lei YU92caa4c2021-02-23 16:59:25 +08003032 hostIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003033 "RequestedHostTransition",
3034 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
3035 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003036 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003037 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003038 // if power button is masked, ignore this
3039 if (!powerButtonMask)
3040 {
3041 sendPowerControlEvent(Event::gracefulPowerOffRequest);
3042 addRestartCause(RestartCause::command);
3043 }
3044 else
3045 {
3046 lg2::info("Power Button Masked.");
3047 throw std::invalid_argument("Transition Request Masked");
3048 return 0;
3049 }
3050 }
3051 else if (requested ==
3052 "xyz.openbmc_project.State.Host.Transition.On")
3053 {
3054 // if power button is masked, ignore this
3055 if (!powerButtonMask)
3056 {
3057 sendPowerControlEvent(Event::powerOnRequest);
3058 addRestartCause(RestartCause::command);
3059 }
3060 else
3061 {
3062 lg2::info("Power Button Masked.");
3063 throw std::invalid_argument("Transition Request Masked");
3064 return 0;
3065 }
3066 }
3067 else if (requested ==
3068 "xyz.openbmc_project.State.Host.Transition.Reboot")
3069 {
3070 // if power button is masked, ignore this
3071 if (!powerButtonMask)
3072 {
3073 sendPowerControlEvent(Event::powerCycleRequest);
3074 addRestartCause(RestartCause::command);
3075 }
3076 else
3077 {
3078 lg2::info("Power Button Masked.");
3079 throw std::invalid_argument("Transition Request Masked");
3080 return 0;
3081 }
3082 }
3083 else if (
3084 requested ==
3085 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3086 {
3087 // if reset button is masked, ignore this
3088 if (!resetButtonMask)
3089 {
3090 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3091 addRestartCause(RestartCause::command);
3092 }
3093 else
3094 {
3095 lg2::info("Reset Button Masked.");
3096 throw std::invalid_argument("Transition Request Masked");
3097 return 0;
3098 }
3099 }
3100 else if (
3101 requested ==
3102 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3103 {
3104 // if reset button is masked, ignore this
3105 if (!resetButtonMask)
3106 {
3107 sendPowerControlEvent(Event::resetRequest);
3108 addRestartCause(RestartCause::command);
3109 }
3110 else
3111 {
3112 lg2::info("Reset Button Masked.");
3113 throw std::invalid_argument("Transition Request Masked");
3114 return 0;
3115 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08003116 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003117 else
3118 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003119 lg2::error("Unrecognized host state transition request.");
3120 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003121 return 0;
3122 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003123 resp = requested;
3124 return 1;
3125 });
Lei YU92caa4c2021-02-23 16:59:25 +08003126 hostIface->register_property("CurrentHostState",
3127 std::string(getHostState(powerState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003128
Lei YU92caa4c2021-02-23 16:59:25 +08003129 hostIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003130
3131 // Chassis Control Service
3132 sdbusplus::asio::object_server chassisServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003133 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003134
3135 // Chassis Control Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003136 chassisIface =
Priyatharshan P70120512020-09-16 18:47:20 +05303137 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003138 "xyz.openbmc_project.State.Chassis");
3139
Lei YU92caa4c2021-02-23 16:59:25 +08003140 chassisIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003141 "RequestedPowerTransition",
3142 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3143 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003144 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003145 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003146 // if power button is masked, ignore this
3147 if (!powerButtonMask)
3148 {
3149 sendPowerControlEvent(Event::powerOffRequest);
3150 addRestartCause(RestartCause::command);
3151 }
3152 else
3153 {
3154 lg2::info("Power Button Masked.");
3155 throw std::invalid_argument("Transition Request Masked");
3156 return 0;
3157 }
3158 }
3159 else if (requested ==
3160 "xyz.openbmc_project.State.Chassis.Transition.On")
3161 {
3162 // if power button is masked, ignore this
3163 if (!powerButtonMask)
3164 {
3165 sendPowerControlEvent(Event::powerOnRequest);
3166 addRestartCause(RestartCause::command);
3167 }
3168 else
3169 {
3170 lg2::info("Power Button Masked.");
3171 throw std::invalid_argument("Transition Request Masked");
3172 return 0;
3173 }
3174 }
3175 else if (requested ==
3176 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3177 {
3178 // if power button is masked, ignore this
3179 if (!powerButtonMask)
3180 {
3181 sendPowerControlEvent(Event::powerCycleRequest);
3182 addRestartCause(RestartCause::command);
3183 }
3184 else
3185 {
3186 lg2::info("Power Button Masked.");
3187 throw std::invalid_argument("Transition Request Masked");
3188 return 0;
3189 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003190 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003191 else
3192 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003193 lg2::error("Unrecognized chassis state transition request.");
3194 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003195 return 0;
3196 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003197 resp = requested;
3198 return 1;
3199 });
Lei YU92caa4c2021-02-23 16:59:25 +08003200 chassisIface->register_property("CurrentPowerState",
3201 std::string(getChassisState(powerState)));
3202 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003203
Lei YU92caa4c2021-02-23 16:59:25 +08003204 chassisIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003205
Vijay Khemka04175c22020-10-09 14:28:11 -07003206#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003207 // Chassis System Service
3208 sdbusplus::asio::object_server chassisSysServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003209 sdbusplus::asio::object_server(conn);
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003210
3211 // Chassis System Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003212 chassisSysIface = chassisSysServer.add_interface(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003213 "/xyz/openbmc_project/state/chassis_system0",
3214 "xyz.openbmc_project.State.Chassis");
3215
Lei YU92caa4c2021-02-23 16:59:25 +08003216 chassisSysIface->register_property(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003217 "RequestedPowerTransition",
3218 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3219 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003220 if (requested ==
3221 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3222 {
3223 systemReset();
3224 addRestartCause(RestartCause::command);
3225 }
3226 else
3227 {
3228 lg2::error(
3229 "Unrecognized chassis system state transition request.");
3230 throw std::invalid_argument("Unrecognized Transition Request");
3231 return 0;
3232 }
3233 resp = requested;
3234 return 1;
3235 });
Lei YU92caa4c2021-02-23 16:59:25 +08003236 chassisSysIface->register_property(
3237 "CurrentPowerState", std::string(getChassisState(powerState)));
3238 chassisSysIface->register_property("LastStateChangeTime",
3239 getCurrentTimeMs());
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003240
Lei YU92caa4c2021-02-23 16:59:25 +08003241 chassisSysIface->initialize();
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003242
Naveen Moses117c34e2021-05-26 20:10:51 +05303243 if (!slotPowerConfig.lineName.empty())
3244 {
3245 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3246 {
3247 return -1;
3248 }
3249
3250 slotPowerState = SlotPowerState::off;
3251 if (slotPowerLine.get_value() > 0)
3252 {
3253 slotPowerState = SlotPowerState::on;
3254 }
3255
3256 chassisSlotIface = chassisSysServer.add_interface(
3257 "/xyz/openbmc_project/state/chassis_system" + node,
3258 "xyz.openbmc_project.State.Chassis");
3259 chassisSlotIface->register_property(
3260 "RequestedPowerTransition",
3261 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3262 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003263 if (requested ==
3264 "xyz.openbmc_project.State.Chassis.Transition.On")
3265 {
3266 slotPowerOn();
3267 }
3268 else if (requested ==
3269 "xyz.openbmc_project.State.Chassis.Transition.Off")
3270 {
3271 slotPowerOff();
3272 }
3273 else if (
3274 requested ==
3275 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3276 {
3277 slotPowerCycle();
3278 }
3279 else
3280 {
3281 lg2::error(
3282 "Unrecognized chassis system state transition request.\n");
3283 throw std::invalid_argument(
3284 "Unrecognized Transition Request");
3285 return 0;
3286 }
3287 resp = requested;
3288 return 1;
3289 });
Naveen Moses117c34e2021-05-26 20:10:51 +05303290 chassisSlotIface->register_property(
3291 "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3292 chassisSlotIface->register_property("LastStateChangeTime",
3293 getCurrentTimeMs());
3294 chassisSlotIface->initialize();
3295 }
3296#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003297 // Buttons Service
3298 sdbusplus::asio::object_server buttonsServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003299 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003300
Priyatharshan P70120512020-09-16 18:47:20 +05303301 if (!powerButtonConfig.lineName.empty())
John Wang6c090072020-09-30 13:32:16 +08003302 {
Priyatharshan P70120512020-09-16 18:47:20 +05303303 // Power Button Interface
3304 power_control::powerButtonIface = buttonsServer.add_interface(
3305 "/xyz/openbmc_project/chassis/buttons/power",
3306 "xyz.openbmc_project.Chassis.Buttons");
3307
3308 powerButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003309 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003310 if (requested)
Priyatharshan P70120512020-09-16 18:47:20 +05303311 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003312 if (powerButtonMask)
3313 {
3314 return 1;
3315 }
3316 if (!setGPIOOutput(powerOutConfig.lineName,
3317 !powerOutConfig.polarity,
3318 powerButtonMask))
3319 {
3320 throw std::runtime_error("Failed to request GPIO");
3321 return 0;
3322 }
3323 lg2::info("Power Button Masked.");
Priyatharshan P70120512020-09-16 18:47:20 +05303324 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003325 else
Priyatharshan P70120512020-09-16 18:47:20 +05303326 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003327 if (!powerButtonMask)
3328 {
3329 return 1;
3330 }
3331 lg2::info("Power Button Un-masked");
3332 powerButtonMask.reset();
Priyatharshan P70120512020-09-16 18:47:20 +05303333 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003334 // Update the mask setting
3335 current = requested;
3336 return 1;
3337 });
Priyatharshan P70120512020-09-16 18:47:20 +05303338
3339 // Check power button state
3340 bool powerButtonPressed;
3341 if (powerButtonConfig.type == ConfigType::GPIO)
3342 {
3343 powerButtonPressed = powerButtonLine.get_value() == 0;
3344 }
3345 else
3346 {
3347 powerButtonPressed = getProperty(powerButtonConfig) == 0;
3348 }
3349
3350 powerButtonIface->register_property("ButtonPressed",
3351 powerButtonPressed);
3352
3353 powerButtonIface->initialize();
3354 }
3355
3356 if (!resetButtonConfig.lineName.empty())
3357 {
3358 // Reset Button Interface
3359
Lei YU92caa4c2021-02-23 16:59:25 +08003360 resetButtonIface = buttonsServer.add_interface(
John Wang6c090072020-09-30 13:32:16 +08003361 "/xyz/openbmc_project/chassis/buttons/reset",
3362 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003363
Lei YU92caa4c2021-02-23 16:59:25 +08003364 resetButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003365 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003366 if (requested)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003367 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003368 if (resetButtonMask)
3369 {
3370 return 1;
3371 }
3372 if (!setGPIOOutput(resetOutConfig.lineName,
3373 !resetOutConfig.polarity,
3374 resetButtonMask))
3375 {
3376 throw std::runtime_error("Failed to request GPIO");
3377 return 0;
3378 }
3379 lg2::info("Reset Button Masked.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003380 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003381 else
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003382 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003383 if (!resetButtonMask)
3384 {
3385 return 1;
3386 }
3387 lg2::info("Reset Button Un-masked");
3388 resetButtonMask.reset();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003389 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003390 // Update the mask setting
3391 current = requested;
3392 return 1;
3393 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003394
John Wang6c090072020-09-30 13:32:16 +08003395 // Check reset button state
Priyatharshan P70120512020-09-16 18:47:20 +05303396 bool resetButtonPressed;
3397 if (resetButtonConfig.type == ConfigType::GPIO)
3398 {
3399 resetButtonPressed = resetButtonLine.get_value() == 0;
3400 }
3401 else
3402 {
3403 resetButtonPressed = getProperty(resetButtonConfig) == 0;
3404 }
3405
Lei YU92caa4c2021-02-23 16:59:25 +08003406 resetButtonIface->register_property("ButtonPressed",
3407 resetButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003408
Lei YU92caa4c2021-02-23 16:59:25 +08003409 resetButtonIface->initialize();
John Wang6c090072020-09-30 13:32:16 +08003410 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003411
Lei YU92caa4c2021-02-23 16:59:25 +08003412 if (nmiButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003413 {
3414 // NMI Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003415 nmiButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003416 "/xyz/openbmc_project/chassis/buttons/nmi",
3417 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003418
Lei YU92caa4c2021-02-23 16:59:25 +08003419 nmiButtonIface->register_property(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003420 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003421 if (nmiButtonMasked == requested)
3422 {
3423 // NMI button mask is already set as requested, so no change
3424 return 1;
3425 }
3426 if (requested)
3427 {
3428 lg2::info("NMI Button Masked.");
3429 nmiButtonMasked = true;
3430 }
3431 else
3432 {
3433 lg2::info("NMI Button Un-masked.");
3434 nmiButtonMasked = false;
3435 }
3436 // Update the mask setting
3437 current = nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003438 return 1;
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003439 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003440
Vijay Khemka33a532d2019-11-14 16:50:35 -08003441 // Check NMI button state
Priyatharshan P70120512020-09-16 18:47:20 +05303442 bool nmiButtonPressed;
3443 if (nmiButtonConfig.type == ConfigType::GPIO)
3444 {
3445 nmiButtonPressed = nmiButtonLine.get_value() == 0;
3446 }
3447 else
3448 {
3449 nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3450 }
3451
Lei YU92caa4c2021-02-23 16:59:25 +08003452 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003453
Lei YU92caa4c2021-02-23 16:59:25 +08003454 nmiButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003455 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003456
Lei YU92caa4c2021-02-23 16:59:25 +08003457 if (nmiOutLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003458 {
3459 // NMI out Service
3460 sdbusplus::asio::object_server nmiOutServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003461 sdbusplus::asio::object_server(conn);
Chen Yugang174ec662019-08-19 19:58:49 +08003462
Vijay Khemka33a532d2019-11-14 16:50:35 -08003463 // NMI out Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303464 nmiOutIface = nmiOutServer.add_interface(
3465 "/xyz/openbmc_project/control/host" + node + "/nmi",
3466 "xyz.openbmc_project.Control.Host.NMI");
Lei YU92caa4c2021-02-23 16:59:25 +08003467 nmiOutIface->register_method("NMI", nmiReset);
3468 nmiOutIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003469 }
Chen Yugang174ec662019-08-19 19:58:49 +08003470
Lei YU92caa4c2021-02-23 16:59:25 +08003471 if (idButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003472 {
3473 // ID Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003474 idButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003475 "/xyz/openbmc_project/chassis/buttons/id",
3476 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003477
Vijay Khemka33a532d2019-11-14 16:50:35 -08003478 // Check ID button state
Priyatharshan P70120512020-09-16 18:47:20 +05303479 bool idButtonPressed;
3480 if (idButtonConfig.type == ConfigType::GPIO)
3481 {
3482 idButtonPressed = idButtonLine.get_value() == 0;
3483 }
3484 else
3485 {
3486 idButtonPressed = getProperty(idButtonConfig) == 0;
3487 }
3488
Lei YU92caa4c2021-02-23 16:59:25 +08003489 idButtonIface->register_property("ButtonPressed", idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003490
Lei YU92caa4c2021-02-23 16:59:25 +08003491 idButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003492 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003493
3494 // OS State Service
3495 sdbusplus::asio::object_server osServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003496 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003497
3498 // OS State Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003499 osIface = osServer.add_interface(
Potin Lai33737912024-02-23 09:53:47 +08003500 "/xyz/openbmc_project/state/host" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003501 "xyz.openbmc_project.State.OperatingSystem.Status");
3502
3503 // Get the initial OS state based on POST complete
Jason M. Billsc6d75652024-09-10 16:54:26 -07003504 // Asserted, OS state is "Standby" (ready to boot)
3505 // De-Asserted, OS state is "Inactive"
Tim Lee86239182021-12-23 11:46:01 +08003506 OperatingSystemStateStage osState;
Priyatharshan P70120512020-09-16 18:47:20 +05303507 if (postCompleteConfig.type == ConfigType::GPIO)
3508 {
Jason M. Billsc6d75652024-09-10 16:54:26 -07003509 osState = postCompleteLine.get_value() == postCompleteConfig.polarity
3510 ? OperatingSystemStateStage::Standby
3511 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303512 }
3513 else
3514 {
Tim Lee86239182021-12-23 11:46:01 +08003515 osState = getProperty(postCompleteConfig) > 0
Jason M. Billsc6d75652024-09-10 16:54:26 -07003516 ? OperatingSystemStateStage::Standby
3517 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303518 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003519
Tim Lee86239182021-12-23 11:46:01 +08003520 osIface->register_property(
3521 "OperatingSystemState",
3522 std::string(getOperatingSystemStateStage(osState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003523
Lei YU92caa4c2021-02-23 16:59:25 +08003524 osIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003525
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003526 // Restart Cause Service
3527 sdbusplus::asio::object_server restartCauseServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003528 sdbusplus::asio::object_server(conn);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003529
3530 // Restart Cause Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003531 restartCauseIface = restartCauseServer.add_interface(
Naveen Mosesec972d82021-07-16 21:19:23 +05303532 "/xyz/openbmc_project/control/host" + node + "/restart_cause",
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003533 "xyz.openbmc_project.Control.Host.RestartCause");
3534
Lei YU92caa4c2021-02-23 16:59:25 +08003535 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003536 "RestartCause",
3537 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3538
Lei YU92caa4c2021-02-23 16:59:25 +08003539 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003540 "RequestedRestartCause",
3541 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3542 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003543 if (requested ==
3544 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3545 {
3546 addRestartCause(RestartCause::watchdog);
3547 }
3548 else
3549 {
3550 throw std::invalid_argument(
3551 "Unrecognized RestartCause Request");
3552 return 0;
3553 }
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003554
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003555 lg2::info("RestartCause requested: {RESTART_CAUSE}",
3556 "RESTART_CAUSE", requested);
3557 resp = requested;
3558 return 1;
3559 });
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003560
Lei YU92caa4c2021-02-23 16:59:25 +08003561 restartCauseIface->initialize();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003562
Lei YU92caa4c2021-02-23 16:59:25 +08003563 currentHostStateMonitor();
Yong Li8d660212019-12-27 10:18:10 +08003564
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +03003565 if (!hpmStbyEnConfig.lineName.empty())
3566 {
3567 // Set to indicate BMC's power control module is ready to take
3568 // the inputs [PWR_GOOD] from the HPM FPGA
3569 gpiod::line hpmLine;
3570 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3571 hpmLine))
3572 {
3573 return -1;
3574 }
3575 }
3576
Lei YU92caa4c2021-02-23 16:59:25 +08003577 io.run();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003578
3579 return 0;
3580}