blob: e6a054809a821c796d45ca36bb86c7372b311452 [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
Jayanth Othayoth857c5582025-06-12 11:17:51 -05001593 std::string objectPath = "/xyz/openbmc_project/state/host" + node;
1594
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001595 static auto match = sdbusplus::bus::match_t(
1596 *conn,
1597 "type='signal',member='PropertiesChanged', "
1598 "interface='org.freedesktop.DBus.Properties', "
Jayanth Othayoth857c5582025-06-12 11:17:51 -05001599 "path='" +
1600 objectPath +
1601 "',"
1602 "arg0='xyz.openbmc_project.State.Host'",
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001603 [](sdbusplus::message_t& message) {
1604 std::string intfName;
1605 std::map<std::string, std::variant<std::string>> properties;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001606
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001607 try
1608 {
1609 message.read(intfName, properties);
1610 }
1611 catch (const std::exception& e)
1612 {
1613 lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1614 return;
1615 }
1616 if (properties.empty())
1617 {
1618 lg2::error("ERROR: Empty PropertiesChanged signal received");
1619 return;
1620 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001621
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001622 // We only want to check for CurrentHostState
1623 if (properties.begin()->first != "CurrentHostState")
1624 {
1625 return;
1626 }
1627 std::string* currentHostState =
1628 std::get_if<std::string>(&(properties.begin()->second));
1629 if (currentHostState == nullptr)
1630 {
1631 lg2::error("{PROPERTY} property invalid", "PROPERTY",
1632 properties.begin()->first);
1633 return;
1634 }
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001635
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001636 if (*currentHostState ==
1637 "xyz.openbmc_project.State.Host.HostState.Running")
1638 {
1639 pohCounterTimerStart();
1640 // Clear the restart cause set for the next restart
1641 clearRestartCause();
1642 sd_journal_send("MESSAGE=Host system DC power is on",
1643 "PRIORITY=%i", LOG_INFO,
1644 "REDFISH_MESSAGE_ID=%s",
1645 "OpenBMC.0.1.DCPowerOn", NULL);
1646 }
1647 else
1648 {
1649 pohCounterTimer.cancel();
1650 // POST_COMPLETE GPIO event is not working in some platforms
1651 // when power state is changed to OFF. This resulted in
1652 // 'OperatingSystemState' to stay at 'Standby', even though
1653 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1654 // if HostState is trurned to OFF.
1655 setOperatingSystemState(OperatingSystemStateStage::Inactive);
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301656
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001657 // Set the restart cause set for this restart
1658 setRestartCause();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001659#ifdef USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001660 resetACBootProperty();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001661#endif // USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001662 sd_journal_send("MESSAGE=Host system DC power is off",
1663 "PRIORITY=%i", LOG_INFO,
1664 "REDFISH_MESSAGE_ID=%s",
1665 "OpenBMC.0.1.DCPowerOff", NULL);
1666 }
1667 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001668}
1669
1670static void sioPowerGoodWatchdogTimerStart()
1671{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001672 lg2::info("SIO power good watchdog timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001673 sioPowerGoodWatchdogTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001674 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001675 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code
1676 ec) {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001677 if (ec)
1678 {
1679 // operation_aborted is expected if timer is canceled before
1680 // completion.
1681 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001682 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001683 lg2::error(
1684 "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1685 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001686 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001687 lg2::info("SIO power good watchdog timer canceled");
1688 return;
1689 }
1690 lg2::info("SIO power good watchdog timer completed");
1691 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1692 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001693}
1694
1695static void powerStateOn(const Event event)
1696{
1697 logEvent(__FUNCTION__, event);
1698 switch (event)
1699 {
Jason M. Bills35471132025-05-06 12:26:10 -07001700 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001701 setPowerState(PowerState::off);
1702 // DC power is unexpectedly lost, beep
1703 beep(beepPowerFail);
1704 break;
1705 case Event::sioS5Assert:
1706 setPowerState(PowerState::transitionToOff);
Matt Simmering58e379d2022-09-23 14:45:50 -07001707#if IGNORE_SOFT_RESETS_DURING_POST
1708 // Only recognize soft resets once host gets past POST COMPLETE
1709 if (operatingSystemState != OperatingSystemStateStage::Standby)
1710 {
1711 ignoreNextSoftReset = true;
1712 }
1713#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001714 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001715 break;
Jason M. Billsfb957332021-01-28 13:18:46 -08001716#if USE_PLT_RST
1717 case Event::pltRstAssert:
1718#else
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001719 case Event::postCompleteDeAssert:
Jason M. Billsfb957332021-01-28 13:18:46 -08001720#endif
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001721 setPowerState(PowerState::checkForWarmReset);
Matt Simmering58e379d2022-09-23 14:45:50 -07001722#if IGNORE_SOFT_RESETS_DURING_POST
1723 // Only recognize soft resets once host gets past POST COMPLETE
1724 if (operatingSystemState != OperatingSystemStateStage::Standby)
1725 {
1726 ignoreNextSoftReset = true;
1727 }
1728#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001729 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001730 warmResetCheckTimerStart();
1731 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001732 case Event::powerButtonPressed:
1733 setPowerState(PowerState::gracefulTransitionToOff);
1734 gracefulPowerOffTimerStart();
1735 break;
1736 case Event::powerOffRequest:
1737 setPowerState(PowerState::transitionToOff);
1738 forcePowerOff();
1739 break;
1740 case Event::gracefulPowerOffRequest:
1741 setPowerState(PowerState::gracefulTransitionToOff);
1742 gracefulPowerOffTimerStart();
1743 gracefulPowerOff();
1744 break;
1745 case Event::powerCycleRequest:
1746 setPowerState(PowerState::transitionToCycleOff);
1747 forcePowerOff();
1748 break;
1749 case Event::gracefulPowerCycleRequest:
1750 setPowerState(PowerState::gracefulTransitionToCycleOff);
1751 gracefulPowerOffTimerStart();
1752 gracefulPowerOff();
1753 break;
Jayanth Othayothdc0bab92024-02-07 07:24:35 -06001754 case Event::resetButtonPressed:
1755 setPowerState(PowerState::checkForWarmReset);
1756 warmResetCheckTimerStart();
1757 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001758 case Event::resetRequest:
1759 reset();
1760 break;
1761 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001762 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001763 break;
1764 }
1765}
1766
Jason M. Bills35471132025-05-06 12:26:10 -07001767static void powerStateWaitForPowerOK(const Event event)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001768{
1769 logEvent(__FUNCTION__, event);
1770 switch (event)
1771 {
Jason M. Bills35471132025-05-06 12:26:10 -07001772 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301773 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001774 // Cancel any GPIO assertions held during the transition
1775 gpioAssertTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001776 powerOKWatchdogTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301777 if (sioEnabled == true)
1778 {
1779 sioPowerGoodWatchdogTimerStart();
1780 setPowerState(PowerState::waitForSIOPowerGood);
1781 }
1782 else
1783 {
1784 setPowerState(PowerState::on);
1785 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001786 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301787 }
Jason M. Bills35471132025-05-06 12:26:10 -07001788 case Event::powerOKWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001789 setPowerState(PowerState::off);
Jason M. Bills35471132025-05-06 12:26:10 -07001790 powerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001791 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001792 case Event::sioPowerGoodAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001793 powerOKWatchdogTimer.cancel();
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001794 setPowerState(PowerState::on);
1795 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001796 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001797 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001798 break;
1799 }
1800}
1801
1802static void powerStateWaitForSIOPowerGood(const Event event)
1803{
1804 logEvent(__FUNCTION__, event);
1805 switch (event)
1806 {
1807 case Event::sioPowerGoodAssert:
1808 sioPowerGoodWatchdogTimer.cancel();
1809 setPowerState(PowerState::on);
1810 break;
1811 case Event::sioPowerGoodWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001812 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001813 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001814 break;
1815 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001816 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001817 break;
1818 }
1819}
1820
1821static void powerStateOff(const Event event)
1822{
1823 logEvent(__FUNCTION__, event);
1824 switch (event)
1825 {
Jason M. Bills35471132025-05-06 12:26:10 -07001826 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301827 {
1828 if (sioEnabled == true)
1829 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001830 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301831 setPowerState(PowerState::waitForSIOPowerGood);
1832 }
1833 else
1834 {
1835 setPowerState(PowerState::on);
1836 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001837 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301838 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001839 case Event::sioS5DeAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001840 powerOKWatchdogTimerStart();
1841 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001842 break;
Jason M. Bills273d7892020-06-17 14:46:57 -07001843 case Event::sioPowerGoodAssert:
1844 setPowerState(PowerState::on);
1845 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001846 case Event::powerButtonPressed:
Jason M. Bills35471132025-05-06 12:26:10 -07001847 powerOKWatchdogTimerStart();
1848 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001849 break;
1850 case Event::powerOnRequest:
Jason M. Bills35471132025-05-06 12:26:10 -07001851 powerOKWatchdogTimerStart();
1852 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001853 powerOn();
1854 break;
1855 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001856 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001857 break;
1858 }
1859}
1860
1861static void powerStateTransitionToOff(const Event event)
1862{
1863 logEvent(__FUNCTION__, event);
1864 switch (event)
1865 {
Jason M. Bills35471132025-05-06 12:26:10 -07001866 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001867 // Cancel any GPIO assertions held during the transition
1868 gpioAssertTimer.cancel();
1869 setPowerState(PowerState::off);
1870 break;
1871 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001872 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001873 break;
1874 }
1875}
1876
1877static void powerStateGracefulTransitionToOff(const Event event)
1878{
1879 logEvent(__FUNCTION__, event);
1880 switch (event)
1881 {
Jason M. Bills35471132025-05-06 12:26:10 -07001882 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001883 gracefulPowerOffTimer.cancel();
1884 setPowerState(PowerState::off);
1885 break;
1886 case Event::gracefulPowerOffTimerExpired:
1887 setPowerState(PowerState::on);
1888 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001889 case Event::powerOffRequest:
1890 gracefulPowerOffTimer.cancel();
1891 setPowerState(PowerState::transitionToOff);
1892 forcePowerOff();
1893 break;
1894 case Event::powerCycleRequest:
1895 gracefulPowerOffTimer.cancel();
1896 setPowerState(PowerState::transitionToCycleOff);
1897 forcePowerOff();
1898 break;
1899 case Event::resetRequest:
1900 gracefulPowerOffTimer.cancel();
1901 setPowerState(PowerState::on);
1902 reset();
1903 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001904 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001905 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001906 break;
1907 }
1908}
1909
1910static void powerStateCycleOff(const Event event)
1911{
1912 logEvent(__FUNCTION__, event);
1913 switch (event)
1914 {
Jason M. Bills35471132025-05-06 12:26:10 -07001915 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301916 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001917 powerCycleTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301918 if (sioEnabled == true)
1919 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001920 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301921 setPowerState(PowerState::waitForSIOPowerGood);
1922 }
1923 else
1924 {
1925 setPowerState(PowerState::on);
1926 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001927 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301928 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001929 case Event::sioS5DeAssert:
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;
1934 case Event::powerButtonPressed:
1935 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001936 powerOKWatchdogTimerStart();
1937 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001938 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001939 case Event::powerCycleTimerExpired:
Jason M. Bills35471132025-05-06 12:26:10 -07001940 powerOKWatchdogTimerStart();
1941 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001942 powerOn();
1943 break;
1944 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001945 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001946 break;
1947 }
1948}
1949
1950static void powerStateTransitionToCycleOff(const Event event)
1951{
1952 logEvent(__FUNCTION__, event);
1953 switch (event)
1954 {
Jason M. Bills35471132025-05-06 12:26:10 -07001955 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001956 // Cancel any GPIO assertions held during the transition
1957 gpioAssertTimer.cancel();
1958 setPowerState(PowerState::cycleOff);
1959 powerCycleTimerStart();
1960 break;
1961 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001962 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001963 break;
1964 }
1965}
1966
1967static void powerStateGracefulTransitionToCycleOff(const Event event)
1968{
1969 logEvent(__FUNCTION__, event);
1970 switch (event)
1971 {
Jason M. Bills35471132025-05-06 12:26:10 -07001972 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001973 gracefulPowerOffTimer.cancel();
1974 setPowerState(PowerState::cycleOff);
1975 powerCycleTimerStart();
1976 break;
1977 case Event::gracefulPowerOffTimerExpired:
1978 setPowerState(PowerState::on);
1979 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001980 case Event::powerOffRequest:
1981 gracefulPowerOffTimer.cancel();
1982 setPowerState(PowerState::transitionToOff);
1983 forcePowerOff();
1984 break;
1985 case Event::powerCycleRequest:
1986 gracefulPowerOffTimer.cancel();
1987 setPowerState(PowerState::transitionToCycleOff);
1988 forcePowerOff();
1989 break;
1990 case Event::resetRequest:
1991 gracefulPowerOffTimer.cancel();
1992 setPowerState(PowerState::on);
1993 reset();
1994 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001995 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001996 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001997 break;
1998 }
1999}
2000
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002001static void powerStateCheckForWarmReset(const Event event)
2002{
2003 logEvent(__FUNCTION__, event);
2004 switch (event)
2005 {
2006 case Event::sioS5Assert:
2007 warmResetCheckTimer.cancel();
2008 setPowerState(PowerState::transitionToOff);
2009 break;
2010 case Event::warmResetDetected:
2011 setPowerState(PowerState::on);
2012 break;
Jason M. Bills35471132025-05-06 12:26:10 -07002013 case Event::powerOKDeAssert:
P.K. Lee344dae82019-11-27 16:35:05 +08002014 warmResetCheckTimer.cancel();
2015 setPowerState(PowerState::off);
2016 // DC power is unexpectedly lost, beep
2017 beep(beepPowerFail);
2018 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002019 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002020 lg2::info("No action taken.");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002021 break;
2022 }
2023}
2024
Jason M. Bills35471132025-05-06 12:26:10 -07002025static void powerOKHandler(bool state)
Zev Weiss584aa132021-09-02 19:21:52 -05002026{
Zev Weissedc86f32024-05-07 01:44:33 +00002027 Event powerControlEvent = (state == powerOkConfig.polarity)
Jason M. Bills35471132025-05-06 12:26:10 -07002028 ? Event::powerOKAssert
2029 : Event::powerOKDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002030 sendPowerControlEvent(powerControlEvent);
2031}
2032
Zev Weiss584aa132021-09-02 19:21:52 -05002033static void sioPowerGoodHandler(bool state)
2034{
Zev Weissedc86f32024-05-07 01:44:33 +00002035 Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2036 ? Event::sioPowerGoodAssert
2037 : Event::sioPowerGoodDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002038 sendPowerControlEvent(powerControlEvent);
2039}
2040
Zev Weiss584aa132021-09-02 19:21:52 -05002041static void sioOnControlHandler(bool state)
2042{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002043 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2044 static_cast<int>(state));
Zev Weiss584aa132021-09-02 19:21:52 -05002045}
2046
Zev Weiss584aa132021-09-02 19:21:52 -05002047static void sioS5Handler(bool state)
2048{
Zev Weissedc86f32024-05-07 01:44:33 +00002049 Event powerControlEvent = (state == sioS5Config.polarity)
2050 ? Event::sioS5Assert
2051 : Event::sioS5DeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002052 sendPowerControlEvent(powerControlEvent);
2053}
2054
Zev Weiss584aa132021-09-02 19:21:52 -05002055static void powerButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002056{
Zev Weissedc86f32024-05-07 01:44:33 +00002057 bool asserted = state == powerButtonConfig.polarity;
2058 powerButtonIface->set_property("ButtonPressed", asserted);
2059 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002060 {
2061 powerButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002062 if (!powerButtonMask)
2063 {
2064 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002065 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002066 }
2067 else
2068 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002069 lg2::info("power button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002070 }
2071 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002072#if USE_BUTTON_PASSTHROUGH
2073 gpiod::line gpioLine;
2074 bool outputState =
2075 asserted ? powerOutConfig.polarity : (!powerOutConfig.polarity);
2076 if (!setGPIOOutput(powerOutConfig.lineName, outputState, gpioLine))
2077 {
2078 lg2::error("{GPIO_NAME} power button passthrough failed", "GPIO_NAME",
2079 powerOutConfig.lineName);
2080 }
2081#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002082}
2083
Zev Weiss584aa132021-09-02 19:21:52 -05002084static void resetButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002085{
Zev Weissedc86f32024-05-07 01:44:33 +00002086 bool asserted = state == resetButtonConfig.polarity;
2087 resetButtonIface->set_property("ButtonPressed", asserted);
2088 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002089 {
2090 resetButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002091 if (!resetButtonMask)
2092 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002093 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002094 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002095 }
2096 else
2097 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002098 lg2::info("reset button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002099 }
2100 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002101#if USE_BUTTON_PASSTHROUGH
2102 gpiod::line gpioLine;
2103 bool outputState =
2104 asserted ? resetOutConfig.polarity : (!resetOutConfig.polarity);
2105 if (!setGPIOOutput(resetOutConfig.lineName, outputState, gpioLine))
2106 {
2107 lg2::error("{GPIO_NAME} reset button passthrough failed", "GPIO_NAME",
2108 resetOutConfig.lineName);
2109 }
2110#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002111}
2112
Vijay Khemka04175c22020-10-09 14:28:11 -07002113#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002114static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2115static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2116static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2117static constexpr auto systemTargetName = "chassis-system-reset.target";
2118
2119void systemReset()
2120{
2121 conn->async_method_call(
2122 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002123 if (ec)
2124 {
2125 lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2126 ec.message());
2127 }
2128 },
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002129 systemdBusname, systemdPath, systemdInterface, "StartUnit",
2130 systemTargetName, "replace");
2131}
Vijay Khemka04175c22020-10-09 14:28:11 -07002132#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002133
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002134static void nmiSetEnableProperty(bool value)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002135{
2136 conn->async_method_call(
2137 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002138 if (ec)
2139 {
2140 lg2::error("failed to set NMI source");
2141 }
2142 },
Chen Yugang303bd582019-11-01 08:45:06 +08002143 "xyz.openbmc_project.Settings",
2144 "/xyz/openbmc_project/Chassis/Control/NMISource",
2145 "org.freedesktop.DBus.Properties", "Set",
2146 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2147 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002148}
2149
2150static void nmiReset(void)
2151{
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002152 const static constexpr int nmiOutPulseTimeMs = 200;
2153
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002154 lg2::info("NMI out action");
Jian Zhang461a1662022-09-22 11:29:01 +08002155 nmiOutLine.set_value(nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002156 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
Jian Zhang461a1662022-09-22 11:29:01 +08002157 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002158 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2159 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2160 // restore the NMI_OUT GPIO line back to the opposite value
Jian Zhang461a1662022-09-22 11:29:01 +08002161 nmiOutLine.set_value(!nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002162 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002163 if (ec)
2164 {
2165 // operation_aborted is expected if timer is canceled before
2166 // completion.
2167 if (ec != boost::asio::error::operation_aborted)
2168 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002169 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2170 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2171 ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002172 }
2173 }
2174 });
2175 // log to redfish
2176 nmiDiagIntLog();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002177 lg2::info("NMI out action completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002178 // reset Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002179 nmiSetEnableProperty(false);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002180}
2181
2182static void nmiSourcePropertyMonitor(void)
2183{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002184 lg2::info("NMI Source Property Monitor");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002185
Patrick Williams439b9c32022-07-22 19:26:53 -05002186 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2187 std::make_unique<sdbusplus::bus::match_t>(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002188 *conn,
2189 "type='signal',interface='org.freedesktop.DBus.Properties',"
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002190 "member='PropertiesChanged',"
2191 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
Patrick Williams439b9c32022-07-22 19:26:53 -05002192 [](sdbusplus::message_t& msg) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002193 std::string interfaceName;
2194 boost::container::flat_map<std::string,
2195 std::variant<bool, std::string>>
2196 propertiesChanged;
2197 std::string state;
2198 bool value = true;
2199 try
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002200 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002201 msg.read(interfaceName, propertiesChanged);
2202 if (propertiesChanged.begin()->first == "Enabled")
2203 {
2204 value =
2205 std::get<bool>(propertiesChanged.begin()->second);
2206 lg2::info(
2207 "NMI Enabled propertiesChanged value: {VALUE}",
2208 "VALUE", value);
2209 nmiEnabled = value;
2210 if (nmiEnabled)
2211 {
2212 nmiReset();
2213 }
2214 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002215 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002216 catch (const std::exception& e)
2217 {
2218 lg2::error("Unable to read NMI source: {ERROR}", "ERROR",
2219 e);
2220 return;
2221 }
2222 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002223}
2224
2225static void setNmiSource()
2226{
2227 conn->async_method_call(
2228 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002229 if (ec)
2230 {
2231 lg2::error("failed to set NMI source");
2232 }
2233 },
Chen Yugang303bd582019-11-01 08:45:06 +08002234 "xyz.openbmc_project.Settings",
2235 "/xyz/openbmc_project/Chassis/Control/NMISource",
2236 "org.freedesktop.DBus.Properties", "Set",
2237 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
Jason M. Bills418ce112021-09-08 15:15:05 -07002238 std::variant<std::string>{
Tim Lee6af569f2024-03-11 17:32:42 +08002239 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002240 // set Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002241 nmiSetEnableProperty(true);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002242}
2243
Zev Weiss584aa132021-09-02 19:21:52 -05002244static void nmiButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002245{
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002246 // Don't handle event if host not running and config doesn't force it
2247 if (!nmiWhenPoweredOff &&
2248 getHostState(powerState) !=
2249 "xyz.openbmc_project.State.Host.HostState.Running")
2250 {
2251 return;
2252 }
Zev Weissedc86f32024-05-07 01:44:33 +00002253
2254 bool asserted = state == nmiButtonConfig.polarity;
2255 nmiButtonIface->set_property("ButtonPressed", asserted);
2256 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002257 {
2258 nmiButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002259 if (nmiButtonMasked)
2260 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002261 lg2::info("NMI button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002262 }
2263 else
2264 {
2265 setNmiSource();
2266 }
2267 }
Zev Weiss584aa132021-09-02 19:21:52 -05002268}
2269
Zev Weiss584aa132021-09-02 19:21:52 -05002270static void idButtonHandler(bool state)
2271{
Zev Weissedc86f32024-05-07 01:44:33 +00002272 bool asserted = state == idButtonConfig.polarity;
2273 idButtonIface->set_property("ButtonPressed", asserted);
Zev Weiss584aa132021-09-02 19:21:52 -05002274}
2275
Jason M. Billsfb957332021-01-28 13:18:46 -08002276static void pltRstHandler(bool pltRst)
2277{
2278 if (pltRst)
2279 {
2280 sendPowerControlEvent(Event::pltRstDeAssert);
2281 }
2282 else
2283 {
2284 sendPowerControlEvent(Event::pltRstAssert);
2285 }
2286}
2287
Patrick Williams439b9c32022-07-22 19:26:53 -05002288[[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
Jason M. Billsfb957332021-01-28 13:18:46 -08002289{
2290 std::string interfaceName;
2291 boost::container::flat_map<std::string, std::variant<bool>>
2292 propertiesChanged;
Jason M. Billsfb957332021-01-28 13:18:46 -08002293 try
2294 {
2295 msg.read(interfaceName, propertiesChanged);
2296 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002297 catch (const std::exception& e)
Jason M. Billsfb957332021-01-28 13:18:46 -08002298 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002299 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
Jason M. Billsfb957332021-01-28 13:18:46 -08002300 return;
2301 }
2302 if (propertiesChanged.empty())
2303 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002304 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
Jason M. Billsfb957332021-01-28 13:18:46 -08002305 return;
2306 }
2307
2308 for (auto& [property, value] : propertiesChanged)
2309 {
2310 if (property == "ESpiPlatformReset")
2311 {
2312 bool* pltRst = std::get_if<bool>(&value);
2313 if (pltRst == nullptr)
2314 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002315 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
Jason M. Billsfb957332021-01-28 13:18:46 -08002316 return;
2317 }
2318 pltRstHandler(*pltRst);
2319 }
2320 }
2321}
2322
Zev Weiss584aa132021-09-02 19:21:52 -05002323static void postCompleteHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002324{
Zev Weissedc86f32024-05-07 01:44:33 +00002325 bool asserted = state == postCompleteConfig.polarity;
2326 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002327 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002328 sendPowerControlEvent(Event::postCompleteAssert);
Tim Lee86239182021-12-23 11:46:01 +08002329 setOperatingSystemState(OperatingSystemStateStage::Standby);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002330 }
2331 else
2332 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002333 sendPowerControlEvent(Event::postCompleteDeAssert);
Tim Lee86239182021-12-23 11:46:01 +08002334 setOperatingSystemState(OperatingSystemStateStage::Inactive);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002335 }
Zev Weiss584aa132021-09-02 19:21:52 -05002336}
2337
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302338static int loadConfigValues()
2339{
2340 const std::string configFilePath =
2341 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2342 ".json";
2343 std::ifstream configFile(configFilePath.c_str());
2344 if (!configFile.is_open())
2345 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002346 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2347 "PATH", configFilePath);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302348 return -1;
2349 }
Zev Weiss1aa08b22021-09-15 17:06:20 -05002350 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302351
Priyatharshan P70120512020-09-16 18:47:20 +05302352 if (jsonData.is_discarded())
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302353 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002354 lg2::error("Power config readings JSON parser failure");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302355 return -1;
2356 }
Priyatharshan P70120512020-09-16 18:47:20 +05302357 auto gpios = jsonData["gpio_configs"];
2358 auto timers = jsonData["timing_configs"];
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302359
Priyatharshan P70120512020-09-16 18:47:20 +05302360 ConfigData* tempGpioData;
2361
2362 for (nlohmann::json& gpioConfig : gpios)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302363 {
Priyatharshan P70120512020-09-16 18:47:20 +05302364 if (!gpioConfig.contains("Name"))
2365 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002366 lg2::error("The 'Name' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302367 return -1;
2368 }
2369
2370 // Iterate through the powersignal map to check if the gpio json config
2371 // entry is valid
2372 std::string gpioName = gpioConfig["Name"];
2373 auto signalMapIter = powerSignalMap.find(gpioName);
2374 if (signalMapIter == powerSignalMap.end())
2375 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002376 lg2::error(
2377 "{GPIO_NAME} is not a recognized power-control signal name",
2378 "GPIO_NAME", gpioName);
Priyatharshan P70120512020-09-16 18:47:20 +05302379 return -1;
2380 }
2381
2382 // assign the power signal name to the corresponding structure reference
2383 // from map then fillup the structure with coressponding json config
2384 // value
2385 tempGpioData = signalMapIter->second;
2386 tempGpioData->name = gpioName;
2387
2388 if (!gpioConfig.contains("Type"))
2389 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002390 lg2::error("The \'Type\' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302391 return -1;
2392 }
2393
2394 std::string signalType = gpioConfig["Type"];
2395 if (signalType == "GPIO")
2396 {
2397 tempGpioData->type = ConfigType::GPIO;
2398 }
2399 else if (signalType == "DBUS")
2400 {
2401 tempGpioData->type = ConfigType::DBUS;
2402 }
2403 else
2404 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002405 lg2::error("{TYPE} is not a recognized power-control signal type",
2406 "TYPE", signalType);
Priyatharshan P70120512020-09-16 18:47:20 +05302407 return -1;
2408 }
2409
2410 if (tempGpioData->type == ConfigType::GPIO)
2411 {
2412 if (gpioConfig.contains("LineName"))
2413 {
2414 tempGpioData->lineName = gpioConfig["LineName"];
2415 }
2416 else
2417 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002418 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002419 "The \'LineName\' field must be defined for GPIO configuration");
Priyatharshan P70120512020-09-16 18:47:20 +05302420 return -1;
2421 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002422 if (gpioConfig.contains("Polarity"))
2423 {
2424 std::string polarity = gpioConfig["Polarity"];
2425 if (polarity == "ActiveLow")
2426 {
2427 tempGpioData->polarity = false;
2428 }
2429 else if (polarity == "ActiveHigh")
2430 {
2431 tempGpioData->polarity = true;
2432 }
2433 else
2434 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002435 lg2::error(
2436 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2437 "POLARITY", polarity);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002438 return -1;
2439 }
2440 }
2441 else
2442 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002443 lg2::error("Polarity field not found for {GPIO_NAME}",
2444 "GPIO_NAME", tempGpioData->lineName);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002445 return -1;
2446 }
Priyatharshan P70120512020-09-16 18:47:20 +05302447 }
2448 else
2449 {
2450 // if dbus based gpio config is defined read and update the dbus
2451 // params corresponding to the gpio config instance
2452 for (auto& [key, dbusParamName] : dbusParams)
2453 {
Logananth Sundararaja4308042021-10-20 11:52:05 +05302454 if (!gpioConfig.contains(dbusParamName))
Priyatharshan P70120512020-09-16 18:47:20 +05302455 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002456 lg2::error(
2457 "The {DBUS_NAME} field must be defined for Dbus configuration ",
2458 "DBUS_NAME", dbusParamName);
Priyatharshan P70120512020-09-16 18:47:20 +05302459 return -1;
2460 }
2461 }
Logananth Sundararaja4308042021-10-20 11:52:05 +05302462 tempGpioData->dbusName =
2463 gpioConfig[dbusParams[DbusConfigType::name]];
2464 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
Priyatharshan P70120512020-09-16 18:47:20 +05302465 tempGpioData->interface =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302466 gpioConfig[dbusParams[DbusConfigType::interface]];
Priyatharshan P70120512020-09-16 18:47:20 +05302467 tempGpioData->lineName =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302468 gpioConfig[dbusParams[DbusConfigType::property]];
Zev Weissca478552024-06-11 23:45:58 +00002469
Zev Weissd603dd12024-06-19 21:50:52 +00002470 // dbus-based inputs must be active-high.
2471 tempGpioData->polarity = true;
2472
Zev Weissca478552024-06-11 23:45:58 +00002473 // MatchRegex is optional
2474 auto item = gpioConfig.find("MatchRegex");
2475 if (item != gpioConfig.end())
2476 {
2477 try
2478 {
2479 tempGpioData->matchRegex = std::regex(*item);
2480 }
2481 catch (const std::regex_error& e)
2482 {
2483 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2484 gpioName, "ERR", e.what());
2485 return -1;
2486 }
2487 }
Priyatharshan P70120512020-09-16 18:47:20 +05302488 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302489 }
2490
Priyatharshan P70120512020-09-16 18:47:20 +05302491 // read and store the timer values from json config to Timer Map
2492 for (auto& [key, timerValue] : TimerMap)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302493 {
Priyatharshan P70120512020-09-16 18:47:20 +05302494 if (timers.contains(key.c_str()))
2495 {
2496 timerValue = timers[key.c_str()];
2497 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302498 }
2499
Jonathan Doman891bbde2023-05-17 13:58:24 -07002500 // If "events_configs" key is not in json config, fallback to null
2501 auto events = jsonData.value("event_configs",
2502 nlohmann::json(nlohmann::json::value_t::null));
2503 if (events.is_object())
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002504 {
2505 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2506 }
2507
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302508 return 0;
2509}
Zev Weissa8f116a2021-09-01 21:08:30 -05002510
Zev Weissca478552024-06-11 23:45:58 +00002511template <typename T>
Patrick Williamsb84e7892025-02-01 08:22:06 -05002512static std::optional<T> getMessageValue(sdbusplus::message_t& msg,
2513 const std::string& name)
Zev Weissa8f116a2021-09-01 21:08:30 -05002514{
Zev Weissa8f116a2021-09-01 21:08:30 -05002515 std::string event;
Zev Weissca478552024-06-11 23:45:58 +00002516 std::string thresholdInterface;
2517 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2518
2519 msg.read(thresholdInterface, propertiesChanged);
2520 if (propertiesChanged.empty())
2521 {
2522 return std::nullopt;
2523 }
2524
2525 event = propertiesChanged.begin()->first;
2526 if (event.empty() || event != name)
2527 {
2528 return std::nullopt;
2529 }
2530
2531 return std::get<T>(propertiesChanged.begin()->second);
2532}
2533
2534static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2535 const ConfigData& config, bool& value)
2536{
Zev Weissa8f116a2021-09-01 21:08:30 -05002537 try
2538 {
Zev Weissca478552024-06-11 23:45:58 +00002539 if (config.matchRegex.has_value())
Zev Weissa8f116a2021-09-01 21:08:30 -05002540 {
Zev Weissca478552024-06-11 23:45:58 +00002541 std::optional<std::string> s =
2542 getMessageValue<std::string>(msg, config.lineName);
2543 if (!s.has_value())
2544 {
2545 return false;
2546 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002547
Zev Weissca478552024-06-11 23:45:58 +00002548 std::smatch m;
2549 value = std::regex_match(s.value(), m, config.matchRegex.value());
2550 }
2551 else
Zev Weissa8f116a2021-09-01 21:08:30 -05002552 {
Zev Weissca478552024-06-11 23:45:58 +00002553 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2554 if (!v.has_value())
2555 {
2556 return false;
2557 }
2558 value = v.value();
Zev Weissa8f116a2021-09-01 21:08:30 -05002559 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002560 return true;
2561 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002562 catch (const std::exception& e)
Zev Weissa8f116a2021-09-01 21:08:30 -05002563 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002564 lg2::error(
2565 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
Zev Weissca478552024-06-11 23:45:58 +00002566 "DBUS_NAME", config.lineName, "ERROR", e);
Zev Weissa8f116a2021-09-01 21:08:30 -05002567 return false;
2568 }
2569}
2570
Patrick Williamsb84e7892025-02-01 08:22:06 -05002571static sdbusplus::bus::match_t dbusGPIOMatcher(
2572 const ConfigData& cfg, std::function<void(bool)> onMatch)
Zev Weissa8f116a2021-09-01 21:08:30 -05002573{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002574 auto pulseEventMatcherCallback =
2575 [&cfg, onMatch](sdbusplus::message_t& msg) {
2576 bool value = false;
2577 if (!getDbusMsgGPIOState(msg, cfg, value))
2578 {
2579 return;
2580 }
2581 onMatch(value);
2582 };
Zev Weissa8f116a2021-09-01 21:08:30 -05002583
Patrick Williams439b9c32022-07-22 19:26:53 -05002584 return sdbusplus::bus::match_t(
2585 static_cast<sdbusplus::bus_t&>(*conn),
Zev Weissa8f116a2021-09-01 21:08:30 -05002586 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2587 "PropertiesChanged',arg0='" +
Zev Weiss1cc79212024-06-19 22:14:57 +00002588 cfg.interface + "',path='" + cfg.path + "',sender='" +
2589 cfg.dbusName + "'",
Zev Weissa8f116a2021-09-01 21:08:30 -05002590 std::move(pulseEventMatcherCallback));
2591}
2592
Michal Orzeledd211e2022-10-28 13:10:16 +02002593// D-Bus property read functions
2594void reschedulePropertyRead(const ConfigData& configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302595
Michal Orzeledd211e2022-10-28 13:10:16 +02002596int getProperty(const ConfigData& configData)
2597{
2598 std::variant<bool> resp;
2599
2600 try
Priyatharshan P70120512020-09-16 18:47:20 +05302601 {
Michal Orzeledd211e2022-10-28 13:10:16 +02002602 auto method = conn->new_method_call(
2603 configData.dbusName.c_str(), configData.path.c_str(),
2604 "org.freedesktop.DBus.Properties", "Get");
2605 method.append(configData.interface.c_str(),
2606 configData.lineName.c_str());
2607
2608 auto reply = conn->call(method);
2609 if (reply.is_method_error())
2610 {
2611 lg2::error(
2612 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
2613 "PROPERTY", configData.lineName, "INTERFACE",
2614 configData.interface, "PATH", configData.path);
2615 return -1;
2616 }
2617
2618 reply.read(resp);
2619 }
2620 catch (const sdbusplus::exception_t& e)
2621 {
2622 lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
2623 configData.lineName, "WHAT", e.what());
2624 reschedulePropertyRead(configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302625 return -1;
2626 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002627
Logananth Sundararaj85e111e2021-11-11 13:13:13 +05302628 auto respValue = std::get_if<bool>(&resp);
Priyatharshan P70120512020-09-16 18:47:20 +05302629 if (!respValue)
2630 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002631 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2632 "PROPERTY", configData.lineName);
Priyatharshan P70120512020-09-16 18:47:20 +05302633 return -1;
2634 }
2635 return (*respValue);
2636}
Michal Orzeledd211e2022-10-28 13:10:16 +02002637
2638void setInitialValue(const ConfigData& configData, bool initialValue)
2639{
2640 if (configData.name == "PowerOk")
2641 {
2642 powerState = (initialValue ? PowerState::on : PowerState::off);
2643 hostIface->set_property("CurrentHostState",
2644 std::string(getHostState(powerState)));
2645 }
2646 else if (configData.name == "PowerButton")
2647 {
2648 powerButtonIface->set_property("ButtonPressed", !initialValue);
2649 }
2650 else if (configData.name == "ResetButton")
2651 {
2652 resetButtonIface->set_property("ButtonPressed", !initialValue);
2653 }
2654 else if (configData.name == "NMIButton")
2655 {
2656 nmiButtonIface->set_property("ButtonPressed", !initialValue);
2657 }
2658 else if (configData.name == "IdButton")
2659 {
2660 idButtonIface->set_property("ButtonPressed", !initialValue);
2661 }
2662 else if (configData.name == "PostComplete")
2663 {
2664 OperatingSystemStateStage osState =
Jason M. Billsc6d75652024-09-10 16:54:26 -07002665 (initialValue == postCompleteConfig.polarity
2666 ? OperatingSystemStateStage::Standby
2667 : OperatingSystemStateStage::Inactive);
Michal Orzeledd211e2022-10-28 13:10:16 +02002668 setOperatingSystemState(osState);
2669 }
2670 else
2671 {
2672 lg2::error("Unknown name {NAME}", "NAME", configData.name);
2673 }
2674}
2675
2676void reschedulePropertyRead(const ConfigData& configData)
2677{
2678 auto item = dBusRetryTimers.find(configData.name);
2679
2680 if (item == dBusRetryTimers.end())
2681 {
2682 auto newItem = dBusRetryTimers.insert(
2683 {configData.name, boost::asio::steady_timer(io)});
2684
2685 if (!newItem.second)
2686 {
2687 lg2::error("Failed to add new timer for {NAME}", "NAME",
2688 configData.name);
2689 return;
2690 }
2691
2692 item = newItem.first;
2693 }
2694
2695 auto& timer = item->second;
2696 timer.expires_after(
2697 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2698 timer.async_wait([&configData](const boost::system::error_code ec) {
2699 if (ec)
2700 {
2701 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2702 configData.name, "MSG", ec.message());
2703 dBusRetryTimers.erase(configData.name);
2704 return;
2705 }
2706
2707 int property = getProperty(configData);
2708
2709 if (property >= 0)
2710 {
2711 setInitialValue(configData, (property > 0));
2712 dBusRetryTimers.erase(configData.name);
2713 }
2714 });
2715}
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002716} // namespace power_control
2717
2718int main(int argc, char* argv[])
2719{
Lei YU92caa4c2021-02-23 16:59:25 +08002720 using namespace power_control;
Priyatharshan P70120512020-09-16 18:47:20 +05302721
2722 if (argc > 1)
2723 {
2724 node = argv[1];
2725 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002726 lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2727 node);
Priyatharshan P70120512020-09-16 18:47:20 +05302728
Lei YU92caa4c2021-02-23 16:59:25 +08002729 conn = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002730
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302731 // Load GPIO's through json config file
Lei YU92caa4c2021-02-23 16:59:25 +08002732 if (loadConfigValues() == -1)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302733 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002734 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302735 }
Naveen Mosesec972d82021-07-16 21:19:23 +05302736 /* Currently for single host based systems additional busname is added
2737 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2738 Going forward for single hosts the old bus name without zero numbering
2739 will be removed when all other applications adapted to the
2740 bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2741
2742 if (node == "0")
2743 {
2744 // Request all the dbus names
2745 conn->request_name(hostDbusName.c_str());
2746 conn->request_name(chassisDbusName.c_str());
2747 conn->request_name(osDbusName.c_str());
2748 conn->request_name(buttonDbusName.c_str());
2749 conn->request_name(nmiDbusName.c_str());
2750 conn->request_name(rstCauseDbusName.c_str());
2751 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302752
Zev Weissc4005bd2021-09-01 22:30:23 -05002753 hostDbusName += node;
2754 chassisDbusName += node;
2755 osDbusName += node;
2756 buttonDbusName += node;
2757 nmiDbusName += node;
2758 rstCauseDbusName += node;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002759
Priyatharshan P70120512020-09-16 18:47:20 +05302760 // Request all the dbus names
2761 conn->request_name(hostDbusName.c_str());
2762 conn->request_name(chassisDbusName.c_str());
2763 conn->request_name(osDbusName.c_str());
2764 conn->request_name(buttonDbusName.c_str());
2765 conn->request_name(nmiDbusName.c_str());
2766 conn->request_name(rstCauseDbusName.c_str());
2767
2768 if (sioPwrGoodConfig.lineName.empty() ||
2769 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
Priyatharshan P19c47a32020-08-12 18:16:43 +05302770 {
Lei YU92caa4c2021-02-23 16:59:25 +08002771 sioEnabled = false;
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002772 lg2::info("SIO control GPIOs not defined, disable SIO support.");
Priyatharshan P19c47a32020-08-12 18:16:43 +05302773 }
2774
Jason M. Bills35471132025-05-06 12:26:10 -07002775 // Request power OK GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302776 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002777 {
Jason M. Bills35471132025-05-06 12:26:10 -07002778 if (!requestGPIOEvents(powerOkConfig.lineName, powerOKHandler,
2779 powerOKLine, powerOKEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302780 {
2781 return -1;
2782 }
2783 }
Priyatharshan P70120512020-09-16 18:47:20 +05302784 else if (powerOkConfig.type == ConfigType::DBUS)
2785 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002786 static sdbusplus::bus::match_t powerOkEventMonitor =
Jason M. Bills35471132025-05-06 12:26:10 -07002787 power_control::dbusGPIOMatcher(powerOkConfig, powerOKHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302788 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302789 else
2790 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002791 lg2::error("PowerOk name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002792 return -1;
2793 }
2794
Lei YU92caa4c2021-02-23 16:59:25 +08002795 if (sioEnabled == true)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002796 {
Priyatharshan P19c47a32020-08-12 18:16:43 +05302797 // Request SIO_POWER_GOOD GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302798 if (sioPwrGoodConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302799 {
Priyatharshan P70120512020-09-16 18:47:20 +05302800 if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002801 sioPowerGoodHandler, sioPowerGoodLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302802 sioPowerGoodEvent))
2803 {
2804 return -1;
2805 }
2806 }
2807 else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2808 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002809 static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002810 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2811 sioPowerGoodHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302812 }
2813 else
2814 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002815 lg2::error(
Priyatharshan P70120512020-09-16 18:47:20 +05302816 "sioPwrGood name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302817 return -1;
2818 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002819
Priyatharshan P19c47a32020-08-12 18:16:43 +05302820 // Request SIO_ONCONTROL GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302821 if (sioOnControlConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302822 {
Priyatharshan P70120512020-09-16 18:47:20 +05302823 if (!requestGPIOEvents(sioOnControlConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002824 sioOnControlHandler, sioOnControlLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302825 sioOnControlEvent))
2826 {
2827 return -1;
2828 }
2829 }
2830 else if (sioOnControlConfig.type == ConfigType::DBUS)
2831 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002832 static sdbusplus::bus::match_t sioOnControlEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002833 power_control::dbusGPIOMatcher(sioOnControlConfig,
2834 sioOnControlHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302835 }
2836 else
2837 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002838 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002839 "sioOnControl name should be configured from jsonconfig file\n");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302840 return -1;
2841 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002842
Priyatharshan P19c47a32020-08-12 18:16:43 +05302843 // Request SIO_S5 GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302844 if (sioS5Config.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302845 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002846 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
Priyatharshan P70120512020-09-16 18:47:20 +05302847 sioS5Line, sioS5Event))
2848 {
2849 return -1;
2850 }
2851 }
2852 else if (sioS5Config.type == ConfigType::DBUS)
2853 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002854 static sdbusplus::bus::match_t sioS5EventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002855 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
Priyatharshan P70120512020-09-16 18:47:20 +05302856 }
2857 else
2858 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002859 lg2::error("sioS5 name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302860 return -1;
2861 }
2862 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002863
2864 // Request POWER_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302865 if (powerButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002866 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002867 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2868 powerButtonLine, powerButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302869 {
2870 return -1;
2871 }
2872 }
Priyatharshan P70120512020-09-16 18:47:20 +05302873 else if (powerButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302874 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002875 static sdbusplus::bus::match_t powerButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002876 power_control::dbusGPIOMatcher(powerButtonConfig,
2877 powerButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002878 }
2879
2880 // Request RESET_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302881 if (resetButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002882 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002883 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2884 resetButtonLine, resetButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302885 {
2886 return -1;
2887 }
2888 }
Priyatharshan P70120512020-09-16 18:47:20 +05302889 else if (resetButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302890 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002891 static sdbusplus::bus::match_t resetButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002892 power_control::dbusGPIOMatcher(resetButtonConfig,
2893 resetButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002894 }
2895
2896 // Request NMI_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302897 if (nmiButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302898 {
Priyatharshan P70120512020-09-16 18:47:20 +05302899 if (!nmiButtonConfig.lineName.empty())
2900 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002901 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302902 nmiButtonLine, nmiButtonEvent);
2903 }
2904 }
2905 else if (nmiButtonConfig.type == ConfigType::DBUS)
2906 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002907 static sdbusplus::bus::match_t nmiButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002908 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302909 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002910
2911 // Request ID_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302912 if (idButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302913 {
Priyatharshan P70120512020-09-16 18:47:20 +05302914 if (!idButtonConfig.lineName.empty())
2915 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002916 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302917 idButtonLine, idButtonEvent);
2918 }
2919 }
2920 else if (idButtonConfig.type == ConfigType::DBUS)
2921 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002922 static sdbusplus::bus::match_t idButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002923 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302924 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002925
Jason M. Billsfb957332021-01-28 13:18:46 -08002926#ifdef USE_PLT_RST
Patrick Williams439b9c32022-07-22 19:26:53 -05002927 sdbusplus::bus::match_t pltRstMatch(
Lei YU92caa4c2021-02-23 16:59:25 +08002928 *conn,
Jason M. Billsfb957332021-01-28 13:18:46 -08002929 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2930 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
Lei YU92caa4c2021-02-23 16:59:25 +08002931 hostMiscHandler);
Jason M. Billsfb957332021-01-28 13:18:46 -08002932#endif
2933
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002934 // Request POST_COMPLETE GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302935 if (postCompleteConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002936 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002937 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2938 postCompleteLine, postCompleteEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302939 {
2940 return -1;
2941 }
2942 }
Priyatharshan P70120512020-09-16 18:47:20 +05302943 else if (postCompleteConfig.type == ConfigType::DBUS)
2944 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002945 static sdbusplus::bus::match_t postCompleteEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002946 power_control::dbusGPIOMatcher(postCompleteConfig,
2947 postCompleteHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302948 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302949 else
2950 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002951 lg2::error(
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002952 "postComplete name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002953 return -1;
2954 }
2955
2956 // initialize NMI_OUT GPIO.
Priyatharshan P70120512020-09-16 18:47:20 +05302957 if (!nmiOutConfig.lineName.empty())
2958 {
Jian Zhang461a1662022-09-22 11:29:01 +08002959 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2960 nmiOutLine);
Priyatharshan P70120512020-09-16 18:47:20 +05302961 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002962
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002963 // Initialize POWER_OUT and RESET_OUT GPIO.
2964 gpiod::line line;
Priyatharshan P70120512020-09-16 18:47:20 +05302965 if (!powerOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002966 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002967 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2968 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302969 {
2970 return -1;
2971 }
2972 }
2973 else
2974 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002975 lg2::error("powerOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002976 return -1;
2977 }
2978
Priyatharshan P70120512020-09-16 18:47:20 +05302979 if (!resetOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002980 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002981 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2982 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302983 {
2984 return -1;
2985 }
2986 }
2987 else
2988 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002989 lg2::error("ResetOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002990 return -1;
2991 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002992 // Release line
2993 line.reset();
2994
Matt Simmering58e379d2022-09-23 14:45:50 -07002995 // Initialize the power state and operating system state
Lei YU92caa4c2021-02-23 16:59:25 +08002996 powerState = PowerState::off;
Matt Simmering58e379d2022-09-23 14:45:50 -07002997 operatingSystemState = OperatingSystemStateStage::Inactive;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002998 // Check power good
Priyatharshan P70120512020-09-16 18:47:20 +05302999
3000 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003001 {
Jason M. Bills35471132025-05-06 12:26:10 -07003002 if (powerOKLine.get_value() > 0 ||
Lei YUa37c2472021-09-26 15:57:12 +08003003 (sioEnabled &&
3004 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
Priyatharshan P70120512020-09-16 18:47:20 +05303005 {
3006 powerState = PowerState::on;
3007 }
3008 }
3009 else
3010 {
3011 if (getProperty(powerOkConfig))
3012 {
3013 powerState = PowerState::on;
3014 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003015 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003016 // Check if we need to start the Power Restore policy
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03003017 if (powerState != PowerState::on)
3018 {
3019 powerRestore.run();
3020 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003021
Lei YU92caa4c2021-02-23 16:59:25 +08003022 if (nmiOutLine)
3023 nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003024
Jason M. Billsc46ebb42021-11-10 11:41:31 -08003025 lg2::info("Initializing power state.");
Lei YU92caa4c2021-02-23 16:59:25 +08003026 logStateTransition(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003027
3028 // Power Control Service
3029 sdbusplus::asio::object_server hostServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003030 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003031
3032 // Power Control Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303033 hostIface =
3034 hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
3035 "xyz.openbmc_project.State.Host");
Vernon Maueryb4d03b12021-05-26 19:11:41 -07003036 // Interface for IPMI/Redfish initiated host state transitions
Lei YU92caa4c2021-02-23 16:59:25 +08003037 hostIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003038 "RequestedHostTransition",
3039 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
3040 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003041 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003042 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003043 // if power button is masked, ignore this
3044 if (!powerButtonMask)
3045 {
3046 sendPowerControlEvent(Event::gracefulPowerOffRequest);
3047 addRestartCause(RestartCause::command);
3048 }
3049 else
3050 {
3051 lg2::info("Power Button Masked.");
3052 throw std::invalid_argument("Transition Request Masked");
3053 return 0;
3054 }
3055 }
3056 else if (requested ==
3057 "xyz.openbmc_project.State.Host.Transition.On")
3058 {
3059 // if power button is masked, ignore this
3060 if (!powerButtonMask)
3061 {
3062 sendPowerControlEvent(Event::powerOnRequest);
3063 addRestartCause(RestartCause::command);
3064 }
3065 else
3066 {
3067 lg2::info("Power Button Masked.");
3068 throw std::invalid_argument("Transition Request Masked");
3069 return 0;
3070 }
3071 }
3072 else if (requested ==
3073 "xyz.openbmc_project.State.Host.Transition.Reboot")
3074 {
3075 // if power button is masked, ignore this
3076 if (!powerButtonMask)
3077 {
3078 sendPowerControlEvent(Event::powerCycleRequest);
3079 addRestartCause(RestartCause::command);
3080 }
3081 else
3082 {
3083 lg2::info("Power Button Masked.");
3084 throw std::invalid_argument("Transition Request Masked");
3085 return 0;
3086 }
3087 }
3088 else if (
3089 requested ==
3090 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3091 {
3092 // if reset button is masked, ignore this
3093 if (!resetButtonMask)
3094 {
3095 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3096 addRestartCause(RestartCause::command);
3097 }
3098 else
3099 {
3100 lg2::info("Reset Button Masked.");
3101 throw std::invalid_argument("Transition Request Masked");
3102 return 0;
3103 }
3104 }
3105 else if (
3106 requested ==
3107 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3108 {
3109 // if reset button is masked, ignore this
3110 if (!resetButtonMask)
3111 {
3112 sendPowerControlEvent(Event::resetRequest);
3113 addRestartCause(RestartCause::command);
3114 }
3115 else
3116 {
3117 lg2::info("Reset Button Masked.");
3118 throw std::invalid_argument("Transition Request Masked");
3119 return 0;
3120 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08003121 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003122 else
3123 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003124 lg2::error("Unrecognized host state transition request.");
3125 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003126 return 0;
3127 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003128 resp = requested;
3129 return 1;
3130 });
Lei YU92caa4c2021-02-23 16:59:25 +08003131 hostIface->register_property("CurrentHostState",
3132 std::string(getHostState(powerState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003133
Lei YU92caa4c2021-02-23 16:59:25 +08003134 hostIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003135
3136 // Chassis Control Service
3137 sdbusplus::asio::object_server chassisServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003138 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003139
3140 // Chassis Control Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003141 chassisIface =
Priyatharshan P70120512020-09-16 18:47:20 +05303142 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003143 "xyz.openbmc_project.State.Chassis");
3144
Lei YU92caa4c2021-02-23 16:59:25 +08003145 chassisIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003146 "RequestedPowerTransition",
3147 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3148 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003149 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003150 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003151 // if power button is masked, ignore this
3152 if (!powerButtonMask)
3153 {
3154 sendPowerControlEvent(Event::powerOffRequest);
3155 addRestartCause(RestartCause::command);
3156 }
3157 else
3158 {
3159 lg2::info("Power Button Masked.");
3160 throw std::invalid_argument("Transition Request Masked");
3161 return 0;
3162 }
3163 }
3164 else if (requested ==
3165 "xyz.openbmc_project.State.Chassis.Transition.On")
3166 {
3167 // if power button is masked, ignore this
3168 if (!powerButtonMask)
3169 {
3170 sendPowerControlEvent(Event::powerOnRequest);
3171 addRestartCause(RestartCause::command);
3172 }
3173 else
3174 {
3175 lg2::info("Power Button Masked.");
3176 throw std::invalid_argument("Transition Request Masked");
3177 return 0;
3178 }
3179 }
3180 else if (requested ==
3181 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3182 {
3183 // if power button is masked, ignore this
3184 if (!powerButtonMask)
3185 {
3186 sendPowerControlEvent(Event::powerCycleRequest);
3187 addRestartCause(RestartCause::command);
3188 }
3189 else
3190 {
3191 lg2::info("Power Button Masked.");
3192 throw std::invalid_argument("Transition Request Masked");
3193 return 0;
3194 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003195 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003196 else
3197 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003198 lg2::error("Unrecognized chassis state transition request.");
3199 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003200 return 0;
3201 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003202 resp = requested;
3203 return 1;
3204 });
Lei YU92caa4c2021-02-23 16:59:25 +08003205 chassisIface->register_property("CurrentPowerState",
3206 std::string(getChassisState(powerState)));
3207 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003208
Lei YU92caa4c2021-02-23 16:59:25 +08003209 chassisIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003210
Vijay Khemka04175c22020-10-09 14:28:11 -07003211#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003212 // Chassis System Service
3213 sdbusplus::asio::object_server chassisSysServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003214 sdbusplus::asio::object_server(conn);
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003215
3216 // Chassis System Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003217 chassisSysIface = chassisSysServer.add_interface(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003218 "/xyz/openbmc_project/state/chassis_system0",
3219 "xyz.openbmc_project.State.Chassis");
3220
Lei YU92caa4c2021-02-23 16:59:25 +08003221 chassisSysIface->register_property(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003222 "RequestedPowerTransition",
3223 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3224 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003225 if (requested ==
3226 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3227 {
3228 systemReset();
3229 addRestartCause(RestartCause::command);
3230 }
3231 else
3232 {
3233 lg2::error(
3234 "Unrecognized chassis system state transition request.");
3235 throw std::invalid_argument("Unrecognized Transition Request");
3236 return 0;
3237 }
3238 resp = requested;
3239 return 1;
3240 });
Lei YU92caa4c2021-02-23 16:59:25 +08003241 chassisSysIface->register_property(
3242 "CurrentPowerState", std::string(getChassisState(powerState)));
3243 chassisSysIface->register_property("LastStateChangeTime",
3244 getCurrentTimeMs());
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003245
Lei YU92caa4c2021-02-23 16:59:25 +08003246 chassisSysIface->initialize();
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003247
Naveen Moses117c34e2021-05-26 20:10:51 +05303248 if (!slotPowerConfig.lineName.empty())
3249 {
3250 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3251 {
3252 return -1;
3253 }
3254
3255 slotPowerState = SlotPowerState::off;
3256 if (slotPowerLine.get_value() > 0)
3257 {
3258 slotPowerState = SlotPowerState::on;
3259 }
3260
3261 chassisSlotIface = chassisSysServer.add_interface(
3262 "/xyz/openbmc_project/state/chassis_system" + node,
3263 "xyz.openbmc_project.State.Chassis");
3264 chassisSlotIface->register_property(
3265 "RequestedPowerTransition",
3266 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3267 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003268 if (requested ==
3269 "xyz.openbmc_project.State.Chassis.Transition.On")
3270 {
3271 slotPowerOn();
3272 }
3273 else if (requested ==
3274 "xyz.openbmc_project.State.Chassis.Transition.Off")
3275 {
3276 slotPowerOff();
3277 }
3278 else if (
3279 requested ==
3280 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3281 {
3282 slotPowerCycle();
3283 }
3284 else
3285 {
3286 lg2::error(
3287 "Unrecognized chassis system state transition request.\n");
3288 throw std::invalid_argument(
3289 "Unrecognized Transition Request");
3290 return 0;
3291 }
3292 resp = requested;
3293 return 1;
3294 });
Naveen Moses117c34e2021-05-26 20:10:51 +05303295 chassisSlotIface->register_property(
3296 "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3297 chassisSlotIface->register_property("LastStateChangeTime",
3298 getCurrentTimeMs());
3299 chassisSlotIface->initialize();
3300 }
3301#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003302 // Buttons Service
3303 sdbusplus::asio::object_server buttonsServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003304 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003305
Priyatharshan P70120512020-09-16 18:47:20 +05303306 if (!powerButtonConfig.lineName.empty())
John Wang6c090072020-09-30 13:32:16 +08003307 {
Priyatharshan P70120512020-09-16 18:47:20 +05303308 // Power Button Interface
3309 power_control::powerButtonIface = buttonsServer.add_interface(
3310 "/xyz/openbmc_project/chassis/buttons/power",
3311 "xyz.openbmc_project.Chassis.Buttons");
3312
3313 powerButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003314 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003315 if (requested)
Priyatharshan P70120512020-09-16 18:47:20 +05303316 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003317 if (powerButtonMask)
3318 {
3319 return 1;
3320 }
3321 if (!setGPIOOutput(powerOutConfig.lineName,
3322 !powerOutConfig.polarity,
3323 powerButtonMask))
3324 {
3325 throw std::runtime_error("Failed to request GPIO");
3326 return 0;
3327 }
3328 lg2::info("Power Button Masked.");
Priyatharshan P70120512020-09-16 18:47:20 +05303329 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003330 else
Priyatharshan P70120512020-09-16 18:47:20 +05303331 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003332 if (!powerButtonMask)
3333 {
3334 return 1;
3335 }
3336 lg2::info("Power Button Un-masked");
3337 powerButtonMask.reset();
Priyatharshan P70120512020-09-16 18:47:20 +05303338 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003339 // Update the mask setting
3340 current = requested;
3341 return 1;
3342 });
Priyatharshan P70120512020-09-16 18:47:20 +05303343
3344 // Check power button state
3345 bool powerButtonPressed;
3346 if (powerButtonConfig.type == ConfigType::GPIO)
3347 {
3348 powerButtonPressed = powerButtonLine.get_value() == 0;
3349 }
3350 else
3351 {
3352 powerButtonPressed = getProperty(powerButtonConfig) == 0;
3353 }
3354
3355 powerButtonIface->register_property("ButtonPressed",
3356 powerButtonPressed);
3357
3358 powerButtonIface->initialize();
3359 }
3360
3361 if (!resetButtonConfig.lineName.empty())
3362 {
3363 // Reset Button Interface
3364
Lei YU92caa4c2021-02-23 16:59:25 +08003365 resetButtonIface = buttonsServer.add_interface(
John Wang6c090072020-09-30 13:32:16 +08003366 "/xyz/openbmc_project/chassis/buttons/reset",
3367 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003368
Lei YU92caa4c2021-02-23 16:59:25 +08003369 resetButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003370 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003371 if (requested)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003372 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003373 if (resetButtonMask)
3374 {
3375 return 1;
3376 }
3377 if (!setGPIOOutput(resetOutConfig.lineName,
3378 !resetOutConfig.polarity,
3379 resetButtonMask))
3380 {
3381 throw std::runtime_error("Failed to request GPIO");
3382 return 0;
3383 }
3384 lg2::info("Reset Button Masked.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003385 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003386 else
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003387 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003388 if (!resetButtonMask)
3389 {
3390 return 1;
3391 }
3392 lg2::info("Reset Button Un-masked");
3393 resetButtonMask.reset();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003394 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003395 // Update the mask setting
3396 current = requested;
3397 return 1;
3398 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003399
John Wang6c090072020-09-30 13:32:16 +08003400 // Check reset button state
Priyatharshan P70120512020-09-16 18:47:20 +05303401 bool resetButtonPressed;
3402 if (resetButtonConfig.type == ConfigType::GPIO)
3403 {
3404 resetButtonPressed = resetButtonLine.get_value() == 0;
3405 }
3406 else
3407 {
3408 resetButtonPressed = getProperty(resetButtonConfig) == 0;
3409 }
3410
Lei YU92caa4c2021-02-23 16:59:25 +08003411 resetButtonIface->register_property("ButtonPressed",
3412 resetButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003413
Lei YU92caa4c2021-02-23 16:59:25 +08003414 resetButtonIface->initialize();
John Wang6c090072020-09-30 13:32:16 +08003415 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003416
Lei YU92caa4c2021-02-23 16:59:25 +08003417 if (nmiButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003418 {
3419 // NMI Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003420 nmiButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003421 "/xyz/openbmc_project/chassis/buttons/nmi",
3422 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003423
Lei YU92caa4c2021-02-23 16:59:25 +08003424 nmiButtonIface->register_property(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003425 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003426 if (nmiButtonMasked == requested)
3427 {
3428 // NMI button mask is already set as requested, so no change
3429 return 1;
3430 }
3431 if (requested)
3432 {
3433 lg2::info("NMI Button Masked.");
3434 nmiButtonMasked = true;
3435 }
3436 else
3437 {
3438 lg2::info("NMI Button Un-masked.");
3439 nmiButtonMasked = false;
3440 }
3441 // Update the mask setting
3442 current = nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003443 return 1;
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003444 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003445
Vijay Khemka33a532d2019-11-14 16:50:35 -08003446 // Check NMI button state
Priyatharshan P70120512020-09-16 18:47:20 +05303447 bool nmiButtonPressed;
3448 if (nmiButtonConfig.type == ConfigType::GPIO)
3449 {
3450 nmiButtonPressed = nmiButtonLine.get_value() == 0;
3451 }
3452 else
3453 {
3454 nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3455 }
3456
Lei YU92caa4c2021-02-23 16:59:25 +08003457 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003458
Lei YU92caa4c2021-02-23 16:59:25 +08003459 nmiButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003460 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003461
Lei YU92caa4c2021-02-23 16:59:25 +08003462 if (nmiOutLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003463 {
3464 // NMI out Service
3465 sdbusplus::asio::object_server nmiOutServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003466 sdbusplus::asio::object_server(conn);
Chen Yugang174ec662019-08-19 19:58:49 +08003467
Vijay Khemka33a532d2019-11-14 16:50:35 -08003468 // NMI out Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303469 nmiOutIface = nmiOutServer.add_interface(
3470 "/xyz/openbmc_project/control/host" + node + "/nmi",
3471 "xyz.openbmc_project.Control.Host.NMI");
Lei YU92caa4c2021-02-23 16:59:25 +08003472 nmiOutIface->register_method("NMI", nmiReset);
3473 nmiOutIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003474 }
Chen Yugang174ec662019-08-19 19:58:49 +08003475
Lei YU92caa4c2021-02-23 16:59:25 +08003476 if (idButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003477 {
3478 // ID Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003479 idButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003480 "/xyz/openbmc_project/chassis/buttons/id",
3481 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003482
Vijay Khemka33a532d2019-11-14 16:50:35 -08003483 // Check ID button state
Priyatharshan P70120512020-09-16 18:47:20 +05303484 bool idButtonPressed;
3485 if (idButtonConfig.type == ConfigType::GPIO)
3486 {
3487 idButtonPressed = idButtonLine.get_value() == 0;
3488 }
3489 else
3490 {
3491 idButtonPressed = getProperty(idButtonConfig) == 0;
3492 }
3493
Lei YU92caa4c2021-02-23 16:59:25 +08003494 idButtonIface->register_property("ButtonPressed", idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003495
Lei YU92caa4c2021-02-23 16:59:25 +08003496 idButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003497 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003498
3499 // OS State Service
3500 sdbusplus::asio::object_server osServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003501 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003502
3503 // OS State Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003504 osIface = osServer.add_interface(
Potin Lai33737912024-02-23 09:53:47 +08003505 "/xyz/openbmc_project/state/host" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003506 "xyz.openbmc_project.State.OperatingSystem.Status");
3507
3508 // Get the initial OS state based on POST complete
Jason M. Billsc6d75652024-09-10 16:54:26 -07003509 // Asserted, OS state is "Standby" (ready to boot)
3510 // De-Asserted, OS state is "Inactive"
Tim Lee86239182021-12-23 11:46:01 +08003511 OperatingSystemStateStage osState;
Priyatharshan P70120512020-09-16 18:47:20 +05303512 if (postCompleteConfig.type == ConfigType::GPIO)
3513 {
Jason M. Billsc6d75652024-09-10 16:54:26 -07003514 osState = postCompleteLine.get_value() == postCompleteConfig.polarity
3515 ? OperatingSystemStateStage::Standby
3516 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303517 }
3518 else
3519 {
Tim Lee86239182021-12-23 11:46:01 +08003520 osState = getProperty(postCompleteConfig) > 0
Jason M. Billsc6d75652024-09-10 16:54:26 -07003521 ? OperatingSystemStateStage::Standby
3522 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303523 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003524
Tim Lee86239182021-12-23 11:46:01 +08003525 osIface->register_property(
3526 "OperatingSystemState",
3527 std::string(getOperatingSystemStateStage(osState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003528
Lei YU92caa4c2021-02-23 16:59:25 +08003529 osIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003530
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003531 // Restart Cause Service
3532 sdbusplus::asio::object_server restartCauseServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003533 sdbusplus::asio::object_server(conn);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003534
3535 // Restart Cause Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003536 restartCauseIface = restartCauseServer.add_interface(
Naveen Mosesec972d82021-07-16 21:19:23 +05303537 "/xyz/openbmc_project/control/host" + node + "/restart_cause",
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003538 "xyz.openbmc_project.Control.Host.RestartCause");
3539
Lei YU92caa4c2021-02-23 16:59:25 +08003540 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003541 "RestartCause",
3542 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3543
Lei YU92caa4c2021-02-23 16:59:25 +08003544 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003545 "RequestedRestartCause",
3546 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3547 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003548 if (requested ==
3549 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3550 {
3551 addRestartCause(RestartCause::watchdog);
3552 }
3553 else
3554 {
3555 throw std::invalid_argument(
3556 "Unrecognized RestartCause Request");
3557 return 0;
3558 }
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003559
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003560 lg2::info("RestartCause requested: {RESTART_CAUSE}",
3561 "RESTART_CAUSE", requested);
3562 resp = requested;
3563 return 1;
3564 });
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003565
Lei YU92caa4c2021-02-23 16:59:25 +08003566 restartCauseIface->initialize();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003567
Lei YU92caa4c2021-02-23 16:59:25 +08003568 currentHostStateMonitor();
Yong Li8d660212019-12-27 10:18:10 +08003569
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +03003570 if (!hpmStbyEnConfig.lineName.empty())
3571 {
3572 // Set to indicate BMC's power control module is ready to take
3573 // the inputs [PWR_GOOD] from the HPM FPGA
3574 gpiod::line hpmLine;
3575 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3576 hpmLine))
3577 {
3578 return -1;
3579 }
3580 }
3581
Lei YU92caa4c2021-02-23 16:59:25 +08003582 io.run();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003583
3584 return 0;
3585}