blob: 73910999b06ad20aaf4f507072d819f59e17a3b0 [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>
Willy Tu4d684112025-08-17 22:15:25 +000030#include <sdbusplus/asio/property.hpp>
Vijay Khemka2b6f4422020-05-29 11:13:23 -070031
32#include <filesystem>
33#include <fstream>
Zev Weissca478552024-06-11 23:45:58 +000034#include <optional>
35#include <regex>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070036#include <string_view>
37
38namespace power_control
39{
Ed Tanous744e9a92023-02-28 13:35:22 -080040static boost::asio::io_context io;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070041std::shared_ptr<sdbusplus::asio::connection> conn;
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +030042PersistentState appState;
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +030043PowerRestoreController powerRestore(io);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053044
45static std::string node = "0";
Andrei Kartashev3efcf372021-12-29 15:32:17 +030046static const std::string appName = "power-control";
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053047
Priyatharshan P70120512020-09-16 18:47:20 +053048enum class DbusConfigType
49{
50 name = 1,
51 path,
52 interface,
53 property
54};
Zev Weissca478552024-06-11 23:45:58 +000055
56// Mandatory config parameters for dbus inputs
Priyatharshan P70120512020-09-16 18:47:20 +053057boost::container::flat_map<DbusConfigType, std::string> dbusParams = {
58 {DbusConfigType::name, "DbusName"},
59 {DbusConfigType::path, "Path"},
60 {DbusConfigType::interface, "Interface"},
61 {DbusConfigType::property, "Property"}};
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053062
Priyatharshan P70120512020-09-16 18:47:20 +053063enum class ConfigType
64{
65 GPIO = 1,
66 DBUS
67};
68
69struct ConfigData
70{
71 std::string name;
72 std::string lineName;
73 std::string dbusName;
74 std::string path;
75 std::string interface;
Zev Weissca478552024-06-11 23:45:58 +000076 std::optional<std::regex> matchRegex;
Jean-Marie Verdun50937e72021-08-31 09:15:49 -070077 bool polarity;
Priyatharshan P70120512020-09-16 18:47:20 +053078 ConfigType type;
79};
80
81static ConfigData powerOutConfig;
82static ConfigData powerOkConfig;
83static ConfigData resetOutConfig;
84static ConfigData nmiOutConfig;
85static ConfigData sioPwrGoodConfig;
86static ConfigData sioOnControlConfig;
87static ConfigData sioS5Config;
88static ConfigData postCompleteConfig;
89static ConfigData powerButtonConfig;
90static ConfigData resetButtonConfig;
91static ConfigData idButtonConfig;
92static ConfigData nmiButtonConfig;
Naveen Moses117c34e2021-05-26 20:10:51 +053093static ConfigData slotPowerConfig;
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +030094static ConfigData hpmStbyEnConfig;
Naveen Moses117c34e2021-05-26 20:10:51 +053095
Priyatharshan P70120512020-09-16 18:47:20 +053096// map for storing list of gpio parameters whose config are to be read from x86
97// power control json config
98boost::container::flat_map<std::string, ConfigData*> powerSignalMap = {
99 {"PowerOut", &powerOutConfig},
100 {"PowerOk", &powerOkConfig},
101 {"ResetOut", &resetOutConfig},
102 {"NMIOut", &nmiOutConfig},
103 {"SioPowerGood", &sioPwrGoodConfig},
104 {"SioOnControl", &sioOnControlConfig},
105 {"SIOS5", &sioS5Config},
106 {"PostComplete", &postCompleteConfig},
107 {"PowerButton", &powerButtonConfig},
108 {"ResetButton", &resetButtonConfig},
109 {"IdButton", &idButtonConfig},
Naveen Moses117c34e2021-05-26 20:10:51 +0530110 {"NMIButton", &nmiButtonConfig},
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +0300111 {"SlotPower", &slotPowerConfig},
112 {"HpmStbyEn", &hpmStbyEnConfig}};
Priyatharshan P70120512020-09-16 18:47:20 +0530113
114static std::string hostDbusName = "xyz.openbmc_project.State.Host";
115static std::string chassisDbusName = "xyz.openbmc_project.State.Chassis";
116static std::string osDbusName = "xyz.openbmc_project.State.OperatingSystem";
117static std::string buttonDbusName = "xyz.openbmc_project.Chassis.Buttons";
118static std::string nmiDbusName = "xyz.openbmc_project.Control.Host.NMI";
119static std::string rstCauseDbusName =
120 "xyz.openbmc_project.Control.Host.RestartCause";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700121static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
122static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
Vijay Khemka04175c22020-10-09 14:28:11 -0700123#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -0700124static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSysIface;
Naveen Moses117c34e2021-05-26 20:10:51 +0530125static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSlotIface;
Vijay Khemka04175c22020-10-09 14:28:11 -0700126#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700127static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
128static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
129static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
130static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
131static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
Chen Yugang174ec662019-08-19 19:58:49 +0800132static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700133static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700134
135static gpiod::line powerButtonMask;
136static gpiod::line resetButtonMask;
137static bool nmiButtonMasked = false;
Matt Simmering58e379d2022-09-23 14:45:50 -0700138#if IGNORE_SOFT_RESETS_DURING_POST
139static bool ignoreNextSoftReset = false;
140#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700141
Priyatharshan P70120512020-09-16 18:47:20 +0530142// This map contains all timer values that are to be read from json config
143boost::container::flat_map<std::string, int> TimerMap = {
Jason M. Billsaeefe042021-09-08 14:56:11 -0700144 {"PowerPulseMs", 200},
145 {"ForceOffPulseMs", 15000},
146 {"ResetPulseMs", 500},
147 {"PowerCycleMs", 5000},
148 {"SioPowerGoodWatchdogMs", 1000},
Jason M. Bills35471132025-05-06 12:26:10 -0700149 {"PowerOKWatchdogMs", 8000},
Jason M. Billsaeefe042021-09-08 14:56:11 -0700150 {"GracefulPowerOffS", (5 * 60)},
151 {"WarmResetCheckMs", 500},
152 {"PowerOffSaveMs", 7000},
Michal Orzeledd211e2022-10-28 13:10:16 +0200153 {"SlotPowerCycleMs", 200},
154 {"DbusGetPropertyRetry", 1000}};
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700155
156static bool nmiEnabled = true;
Olivier FAURAXd7ea2832023-04-14 14:08:02 +0000157static bool nmiWhenPoweredOff = true;
Priyatharshan P19c47a32020-08-12 18:16:43 +0530158static bool sioEnabled = true;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700159
160// Timers
161// Time holding GPIOs asserted
162static boost::asio::steady_timer gpioAssertTimer(io);
163// Time between off and on during a power cycle
164static boost::asio::steady_timer powerCycleTimer(io);
165// Time OS gracefully powering off
166static boost::asio::steady_timer gracefulPowerOffTimer(io);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700167// Time the warm reset check
168static boost::asio::steady_timer warmResetCheckTimer(io);
Jason M. Bills35471132025-05-06 12:26:10 -0700169// Time power OK assertion on power-on
170static boost::asio::steady_timer powerOKWatchdogTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700171// Time SIO power good assertion on power-on
172static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
173// Time power-off state save for power loss tracking
174static boost::asio::steady_timer powerStateSaveTimer(io);
175// POH timer
176static boost::asio::steady_timer pohCounterTimer(io);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700177// Time when to allow restart cause updates
178static boost::asio::steady_timer restartCauseTimer(io);
Naveen Moses117c34e2021-05-26 20:10:51 +0530179static boost::asio::steady_timer slotPowerCycleTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700180
Michal Orzeledd211e2022-10-28 13:10:16 +0200181// Map containing timers used for D-Bus get-property retries
182static boost::container::flat_map<std::string, boost::asio::steady_timer>
183 dBusRetryTimers;
184
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700185// GPIO Lines and Event Descriptors
Jason M. Bills35471132025-05-06 12:26:10 -0700186static gpiod::line powerOKLine;
187static boost::asio::posix::stream_descriptor powerOKEvent(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700188static gpiod::line sioPowerGoodLine;
189static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
190static gpiod::line sioOnControlLine;
191static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
192static gpiod::line sioS5Line;
193static boost::asio::posix::stream_descriptor sioS5Event(io);
194static gpiod::line powerButtonLine;
195static boost::asio::posix::stream_descriptor powerButtonEvent(io);
196static gpiod::line resetButtonLine;
197static boost::asio::posix::stream_descriptor resetButtonEvent(io);
198static gpiod::line nmiButtonLine;
199static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
200static gpiod::line idButtonLine;
201static boost::asio::posix::stream_descriptor idButtonEvent(io);
202static gpiod::line postCompleteLine;
203static boost::asio::posix::stream_descriptor postCompleteEvent(io);
204static gpiod::line nmiOutLine;
Naveen Moses117c34e2021-05-26 20:10:51 +0530205static gpiod::line slotPowerLine;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700206
207static constexpr uint8_t beepPowerFail = 8;
208
209static void beep(const uint8_t& beepPriority)
210{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800211 lg2::info("Beep with priority: {BEEP_PRIORITY}", "BEEP_PRIORITY",
212 beepPriority);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700213
214 conn->async_method_call(
215 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400216 if (ec)
217 {
218 lg2::error(
219 "beep returned error with async_method_call (ec = {ERROR_MSG})",
220 "ERROR_MSG", ec.message());
221 return;
222 }
223 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700224 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
225 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
226}
227
Tim Lee86239182021-12-23 11:46:01 +0800228enum class OperatingSystemStateStage
229{
230 Inactive,
231 Standby,
232};
Matt Simmering58e379d2022-09-23 14:45:50 -0700233static OperatingSystemStateStage operatingSystemState;
Patrick Williamsb84e7892025-02-01 08:22:06 -0500234static constexpr std::string_view getOperatingSystemStateStage(
235 const OperatingSystemStateStage stage)
Tim Lee86239182021-12-23 11:46:01 +0800236{
237 switch (stage)
238 {
239 case OperatingSystemStateStage::Inactive:
240 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
241 break;
242 case OperatingSystemStateStage::Standby:
243 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby";
244 break;
245 default:
246 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
247 break;
248 }
249};
250static void setOperatingSystemState(const OperatingSystemStateStage stage)
251{
Marc Olberding74eb00d2025-11-12 17:29:19 -0800252 if (!osIface)
253 {
254 return;
255 }
256
Matt Simmering58e379d2022-09-23 14:45:50 -0700257 operatingSystemState = stage;
258#if IGNORE_SOFT_RESETS_DURING_POST
259 // If POST complete has asserted set ignoreNextSoftReset to false to avoid
260 // masking soft resets after POST
261 if (operatingSystemState == OperatingSystemStateStage::Standby)
262 {
263 ignoreNextSoftReset = false;
264 }
265#endif
Tim Lee86239182021-12-23 11:46:01 +0800266 osIface->set_property("OperatingSystemState",
267 std::string(getOperatingSystemStateStage(stage)));
268
269 lg2::info("Moving os state to {STATE} stage", "STATE",
270 getOperatingSystemStateStage(stage));
271}
272
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700273enum class PowerState
274{
275 on,
Jason M. Bills35471132025-05-06 12:26:10 -0700276 waitForPowerOK,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700277 waitForSIOPowerGood,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700278 off,
279 transitionToOff,
280 gracefulTransitionToOff,
281 cycleOff,
282 transitionToCycleOff,
283 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700284 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700285};
286static PowerState powerState;
287static std::string getPowerStateName(PowerState state)
288{
289 switch (state)
290 {
291 case PowerState::on:
292 return "On";
293 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700294 case PowerState::waitForPowerOK:
295 return "Wait for Power OK";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700296 break;
297 case PowerState::waitForSIOPowerGood:
298 return "Wait for SIO Power Good";
299 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700300 case PowerState::off:
301 return "Off";
302 break;
303 case PowerState::transitionToOff:
304 return "Transition to Off";
305 break;
306 case PowerState::gracefulTransitionToOff:
307 return "Graceful Transition to Off";
308 break;
309 case PowerState::cycleOff:
310 return "Power Cycle Off";
311 break;
312 case PowerState::transitionToCycleOff:
313 return "Transition to Power Cycle Off";
314 break;
315 case PowerState::gracefulTransitionToCycleOff:
316 return "Graceful Transition to Power Cycle Off";
317 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700318 case PowerState::checkForWarmReset:
319 return "Check for Warm Reset";
320 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700321 default:
322 return "unknown state: " + std::to_string(static_cast<int>(state));
323 break;
324 }
325}
326static void logStateTransition(const PowerState state)
327{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800328 lg2::info("Host{HOST}: Moving to \"{STATE}\" state", "HOST", node, "STATE",
329 getPowerStateName(state));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700330}
331
332enum class Event
333{
Jason M. Bills35471132025-05-06 12:26:10 -0700334 powerOKAssert,
335 powerOKDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700336 sioPowerGoodAssert,
337 sioPowerGoodDeAssert,
338 sioS5Assert,
339 sioS5DeAssert,
Jason M. Billsfb957332021-01-28 13:18:46 -0800340 pltRstAssert,
341 pltRstDeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700342 postCompleteAssert,
343 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700344 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700345 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700346 powerCycleTimerExpired,
Jason M. Bills35471132025-05-06 12:26:10 -0700347 powerOKWatchdogTimerExpired,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700348 sioPowerGoodWatchdogTimerExpired,
349 gracefulPowerOffTimerExpired,
350 powerOnRequest,
351 powerOffRequest,
352 powerCycleRequest,
353 resetRequest,
354 gracefulPowerOffRequest,
355 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700356 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700357};
358static std::string getEventName(Event event)
359{
360 switch (event)
361 {
Jason M. Bills35471132025-05-06 12:26:10 -0700362 case Event::powerOKAssert:
363 return "power OK assert";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700364 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700365 case Event::powerOKDeAssert:
366 return "power OK de-assert";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700367 break;
368 case Event::sioPowerGoodAssert:
369 return "SIO power good assert";
370 break;
371 case Event::sioPowerGoodDeAssert:
372 return "SIO power good de-assert";
373 break;
374 case Event::sioS5Assert:
375 return "SIO S5 assert";
376 break;
377 case Event::sioS5DeAssert:
378 return "SIO S5 de-assert";
379 break;
Jason M. Billsfb957332021-01-28 13:18:46 -0800380 case Event::pltRstAssert:
381 return "PLT_RST assert";
382 break;
383 case Event::pltRstDeAssert:
384 return "PLT_RST de-assert";
385 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700386 case Event::postCompleteAssert:
387 return "POST Complete assert";
388 break;
389 case Event::postCompleteDeAssert:
390 return "POST Complete de-assert";
391 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700392 case Event::powerButtonPressed:
393 return "power button pressed";
394 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700395 case Event::resetButtonPressed:
396 return "reset button pressed";
397 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700398 case Event::powerCycleTimerExpired:
399 return "power cycle timer expired";
400 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700401 case Event::powerOKWatchdogTimerExpired:
402 return "power OK watchdog timer expired";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700403 break;
404 case Event::sioPowerGoodWatchdogTimerExpired:
405 return "SIO power good watchdog timer expired";
406 break;
407 case Event::gracefulPowerOffTimerExpired:
408 return "graceful power-off timer expired";
409 break;
410 case Event::powerOnRequest:
411 return "power-on request";
412 break;
413 case Event::powerOffRequest:
414 return "power-off request";
415 break;
416 case Event::powerCycleRequest:
417 return "power-cycle request";
418 break;
419 case Event::resetRequest:
420 return "reset request";
421 break;
422 case Event::gracefulPowerOffRequest:
423 return "graceful power-off request";
424 break;
425 case Event::gracefulPowerCycleRequest:
426 return "graceful power-cycle request";
427 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700428 case Event::warmResetDetected:
429 return "warm reset detected";
430 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700431 default:
432 return "unknown event: " + std::to_string(static_cast<int>(event));
433 break;
434 }
435}
436static void logEvent(const std::string_view stateHandler, const Event event)
437{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800438 lg2::info("{STATE_HANDLER}: {EVENT} event received", "STATE_HANDLER",
439 stateHandler, "EVENT", getEventName(event));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700440}
441
442// Power state handlers
443static void powerStateOn(const Event event);
Jason M. Bills35471132025-05-06 12:26:10 -0700444static void powerStateWaitForPowerOK(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700445static void powerStateWaitForSIOPowerGood(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700446static void powerStateOff(const Event event);
447static void powerStateTransitionToOff(const Event event);
448static void powerStateGracefulTransitionToOff(const Event event);
449static void powerStateCycleOff(const Event event);
450static void powerStateTransitionToCycleOff(const Event event);
451static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700452static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700453
454static std::function<void(const Event)> getPowerStateHandler(PowerState state)
455{
456 switch (state)
457 {
458 case PowerState::on:
459 return powerStateOn;
460 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700461 case PowerState::waitForPowerOK:
462 return powerStateWaitForPowerOK;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700463 break;
464 case PowerState::waitForSIOPowerGood:
465 return powerStateWaitForSIOPowerGood;
466 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700467 case PowerState::off:
468 return powerStateOff;
469 break;
470 case PowerState::transitionToOff:
471 return powerStateTransitionToOff;
472 break;
473 case PowerState::gracefulTransitionToOff:
474 return powerStateGracefulTransitionToOff;
475 break;
476 case PowerState::cycleOff:
477 return powerStateCycleOff;
478 break;
479 case PowerState::transitionToCycleOff:
480 return powerStateTransitionToCycleOff;
481 break;
482 case PowerState::gracefulTransitionToCycleOff:
483 return powerStateGracefulTransitionToCycleOff;
484 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700485 case PowerState::checkForWarmReset:
486 return powerStateCheckForWarmReset;
487 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700488 default:
Zev Weiss047bcb52020-08-20 21:28:11 +0000489 return nullptr;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700490 break;
491 }
492};
493
494static void sendPowerControlEvent(const Event event)
495{
496 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
497 if (handler == nullptr)
498 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800499 lg2::error("Failed to find handler for power state: {STATE}", "STATE",
500 static_cast<int>(powerState));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700501 return;
502 }
503 handler(event);
504}
505
506static uint64_t getCurrentTimeMs()
507{
508 struct timespec time = {};
509
510 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
511 {
512 return 0;
513 }
514 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
515 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
516
517 return currentTimeMs;
518}
519
520static constexpr std::string_view getHostState(const PowerState state)
521{
522 switch (state)
523 {
524 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700525 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700526 case PowerState::gracefulTransitionToCycleOff:
527 return "xyz.openbmc_project.State.Host.HostState.Running";
528 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700529 case PowerState::waitForPowerOK:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700530 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700531 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700532 case PowerState::transitionToOff:
533 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700534 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700535 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700536 return "xyz.openbmc_project.State.Host.HostState.Off";
537 break;
538 default:
539 return "";
540 break;
541 }
542};
543static constexpr std::string_view getChassisState(const PowerState state)
544{
545 switch (state)
546 {
547 case PowerState::on:
548 case PowerState::transitionToOff:
549 case PowerState::gracefulTransitionToOff:
550 case PowerState::transitionToCycleOff:
551 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700552 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700553 return "xyz.openbmc_project.State.Chassis.PowerState.On";
554 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700555 case PowerState::waitForPowerOK:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700556 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700557 case PowerState::off:
558 case PowerState::cycleOff:
559 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
560 break;
561 default:
562 return "";
563 break;
564 }
565};
Naveen Moses117c34e2021-05-26 20:10:51 +0530566#ifdef CHASSIS_SYSTEM_RESET
567enum class SlotPowerState
568{
569 on,
570 off,
571};
572static SlotPowerState slotPowerState;
573static constexpr std::string_view getSlotState(const SlotPowerState state)
574{
575 switch (state)
576 {
577 case SlotPowerState::on:
578 return "xyz.openbmc_project.State.Chassis.PowerState.On";
579 break;
580 case SlotPowerState::off:
581 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
582 break;
583 default:
584 return "";
585 break;
586 }
587};
588static void setSlotPowerState(const SlotPowerState state)
589{
590 slotPowerState = state;
591 chassisSlotIface->set_property("CurrentPowerState",
592 std::string(getSlotState(slotPowerState)));
593 chassisSlotIface->set_property("LastStateChangeTime", getCurrentTimeMs());
594}
595#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700596static void savePowerState(const PowerState state)
597{
598 powerStateSaveTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -0700599 std::chrono::milliseconds(TimerMap["PowerOffSaveMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700600 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
601 if (ec)
602 {
603 // operation_aborted is expected if timer is canceled before
604 // completion.
605 if (ec != boost::asio::error::operation_aborted)
606 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800607 lg2::error("Power-state save async_wait failed: {ERROR_MSG}",
608 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700609 }
610 return;
611 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300612 appState.set(PersistentState::Params::PowerState,
613 std::string{getChassisState(state)});
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700614 });
615}
616static void setPowerState(const PowerState state)
617{
618 powerState = state;
619 logStateTransition(state);
620
621 hostIface->set_property("CurrentHostState",
622 std::string(getHostState(powerState)));
623
624 chassisIface->set_property("CurrentPowerState",
625 std::string(getChassisState(powerState)));
626 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
627
628 // Save the power state for the restore policy
629 savePowerState(state);
630}
631
632enum class RestartCause
633{
634 command,
635 resetButton,
636 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700637 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700638 powerPolicyOn,
639 powerPolicyRestore,
640 softReset,
641};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700642static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700643static std::string getRestartCause(RestartCause cause)
644{
645 switch (cause)
646 {
647 case RestartCause::command:
648 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
649 break;
650 case RestartCause::resetButton:
651 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
652 break;
653 case RestartCause::powerButton:
654 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
655 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700656 case RestartCause::watchdog:
657 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
658 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700659 case RestartCause::powerPolicyOn:
Jason M. Bills418ce112021-09-08 15:15:05 -0700660 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700661 break;
662 case RestartCause::powerPolicyRestore:
Jason M. Bills418ce112021-09-08 15:15:05 -0700663 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyPreviousState";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700664 break;
665 case RestartCause::softReset:
666 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
667 break;
668 default:
669 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
670 break;
671 }
672}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700673static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700674{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700675 // Add this to the set of causes for this restart
676 causeSet.insert(cause);
677}
678static void clearRestartCause()
679{
680 // Clear the set for the next restart
681 causeSet.clear();
682}
683static void setRestartCauseProperty(const std::string& cause)
684{
Marc Olberding74eb00d2025-11-12 17:29:19 -0800685 if (!restartCauseIface)
686 {
687 return;
688 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800689 lg2::info("RestartCause set to {RESTART_CAUSE}", "RESTART_CAUSE", cause);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700690 restartCauseIface->set_property("RestartCause", cause);
691}
Rashmi RV89f61312020-01-22 15:41:50 +0530692
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300693#ifdef USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530694static void resetACBootProperty()
695{
696 if ((causeSet.contains(RestartCause::command)) ||
697 (causeSet.contains(RestartCause::softReset)))
698 {
699 conn->async_method_call(
700 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400701 if (ec)
702 {
703 lg2::error("failed to reset ACBoot property");
704 }
705 },
Rashmi RV89f61312020-01-22 15:41:50 +0530706 "xyz.openbmc_project.Settings",
707 "/xyz/openbmc_project/control/host0/ac_boot",
708 "org.freedesktop.DBus.Properties", "Set",
709 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
710 std::variant<std::string>{"False"});
711 }
712}
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300713#endif // USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530714
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700715static void setRestartCause()
716{
717 // Determine the actual restart cause based on the set of causes
718 std::string restartCause =
719 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
720 if (causeSet.contains(RestartCause::watchdog))
721 {
722 restartCause = getRestartCause(RestartCause::watchdog);
723 }
724 else if (causeSet.contains(RestartCause::command))
725 {
726 restartCause = getRestartCause(RestartCause::command);
727 }
728 else if (causeSet.contains(RestartCause::resetButton))
729 {
730 restartCause = getRestartCause(RestartCause::resetButton);
731 }
732 else if (causeSet.contains(RestartCause::powerButton))
733 {
734 restartCause = getRestartCause(RestartCause::powerButton);
735 }
736 else if (causeSet.contains(RestartCause::powerPolicyOn))
737 {
738 restartCause = getRestartCause(RestartCause::powerPolicyOn);
739 }
740 else if (causeSet.contains(RestartCause::powerPolicyRestore))
741 {
742 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
743 }
744 else if (causeSet.contains(RestartCause::softReset))
745 {
Matt Simmering58e379d2022-09-23 14:45:50 -0700746#if IGNORE_SOFT_RESETS_DURING_POST
747 if (ignoreNextSoftReset)
748 {
749 ignoreNextSoftReset = false;
750 return;
751 }
752#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700753 restartCause = getRestartCause(RestartCause::softReset);
754 }
755
756 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700757}
758
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700759static void systemPowerGoodFailedLog()
760{
761 sd_journal_send(
762 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
763 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
764 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
Jason M. Billsaeefe042021-09-08 14:56:11 -0700765 TimerMap["SioPowerGoodWatchdogMs"], NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700766}
767
Jason M. Bills35471132025-05-06 12:26:10 -0700768static void powerOKFailedLog()
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700769{
Jason M. Bills35471132025-05-06 12:26:10 -0700770 sd_journal_send("MESSAGE=PowerControl: power okay failed to assert",
771 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
772 "OpenBMC.0.1.SystemPowerOnFailed", NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700773}
774
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700775static void powerRestorePolicyLog()
776{
777 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
778 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
779 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
780}
781
782static void powerButtonPressLog()
783{
784 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
785 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
786 "OpenBMC.0.1.PowerButtonPressed", NULL);
787}
788
789static void resetButtonPressLog()
790{
791 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
792 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
793 "OpenBMC.0.1.ResetButtonPressed", NULL);
794}
795
796static void nmiButtonPressLog()
797{
798 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
799 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
800 "OpenBMC.0.1.NMIButtonPressed", NULL);
801}
802
803static void nmiDiagIntLog()
804{
805 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
806 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
807 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
808}
809
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300810PersistentState::PersistentState()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700811{
812 // create the power control directory if it doesn't exist
813 std::error_code ec;
814 if (!(std::filesystem::create_directories(powerControlDir, ec)))
815 {
816 if (ec.value() != 0)
817 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800818 lg2::error("failed to create {DIR_NAME}: {ERROR_MSG}", "DIR_NAME",
819 powerControlDir.string(), "ERROR_MSG", ec.message());
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300820 throw std::runtime_error("Failed to create state directory");
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700821 }
822 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300823
824 // read saved state, it's ok, if the file doesn't exists
825 std::ifstream appStateStream(powerControlDir / stateFile);
826 if (!appStateStream.is_open())
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700827 {
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300828 lg2::info("Cannot open state file \'{PATH}\'", "PATH",
829 std::string(powerControlDir / stateFile));
830 stateData = nlohmann::json({});
831 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700832 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300833 try
834 {
835 appStateStream >> stateData;
836 if (stateData.is_discarded())
837 {
838 lg2::info("Cannot parse state file \'{PATH}\'", "PATH",
839 std::string(powerControlDir / stateFile));
840 stateData = nlohmann::json({});
841 return;
842 }
843 }
844 catch (const std::exception& ex)
845 {
846 lg2::info("Cannot read state file \'{PATH}\'", "PATH",
847 std::string(powerControlDir / stateFile));
848 stateData = nlohmann::json({});
849 return;
850 }
851}
852PersistentState::~PersistentState()
853{
854 saveState();
855}
856const std::string PersistentState::get(Params parameter)
857{
858 auto val = stateData.find(getName(parameter));
859 if (val != stateData.end())
860 {
861 return val->get<std::string>();
862 }
863 return getDefault(parameter);
864}
865void PersistentState::set(Params parameter, const std::string& value)
866{
867 stateData[getName(parameter)] = value;
868 saveState();
869}
870
871const std::string PersistentState::getName(const Params parameter)
872{
873 switch (parameter)
874 {
875 case Params::PowerState:
876 return "PowerState";
877 }
878 return "";
879}
880const std::string PersistentState::getDefault(const Params parameter)
881{
882 switch (parameter)
883 {
884 case Params::PowerState:
885 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
886 }
887 return "";
888}
889void PersistentState::saveState()
890{
891 std::ofstream appStateStream(powerControlDir / stateFile, std::ios::trunc);
892 if (!appStateStream.is_open())
893 {
894 lg2::error("Cannot write state file \'{PATH}\'", "PATH",
895 std::string(powerControlDir / stateFile));
896 return;
897 }
898 appStateStream << stateData.dump(indentationSize);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700899}
900
Patrick Williams48aa1f02023-05-10 07:50:30 -0500901static constexpr const char* setingsService = "xyz.openbmc_project.Settings";
902static constexpr const char* powerRestorePolicyIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300903 "xyz.openbmc_project.Control.Power.RestorePolicy";
904#ifdef USE_ACBOOT
Patrick Williams48aa1f02023-05-10 07:50:30 -0500905static constexpr const char* powerACBootObject =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300906 "/xyz/openbmc_project/control/host0/ac_boot";
Patrick Williams48aa1f02023-05-10 07:50:30 -0500907static constexpr const char* powerACBootIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300908 "xyz.openbmc_project.Common.ACBoot";
909#endif // USE_ACBOOT
910
911namespace match_rules = sdbusplus::bus::match::rules;
912
913static int powerRestoreConfigHandler(sd_bus_message* m, void* context,
914 sd_bus_error*)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700915{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300916 if (context == nullptr || m == nullptr)
917 {
918 throw std::runtime_error("Invalid match");
919 }
Patrick Williams439b9c32022-07-22 19:26:53 -0500920 sdbusplus::message_t message(m);
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300921 PowerRestoreController* powerRestore =
922 static_cast<PowerRestoreController*>(context);
923
924 if (std::string(message.get_member()) == "InterfacesAdded")
925 {
926 sdbusplus::message::object_path path;
927 boost::container::flat_map<std::string, dbusPropertiesList> data;
928
929 message.read(path, data);
930
931 for (auto& [iface, properties] : data)
932 {
933 if ((iface == powerRestorePolicyIface)
934#ifdef USE_ACBOOT
935 || (iface == powerACBootIface)
936#endif // USE_ACBOOT
937 )
938 {
939 powerRestore->setProperties(properties);
940 }
941 }
942 }
943 else if (std::string(message.get_member()) == "PropertiesChanged")
944 {
945 std::string interfaceName;
946 dbusPropertiesList propertiesChanged;
947
948 message.read(interfaceName, propertiesChanged);
949
950 powerRestore->setProperties(propertiesChanged);
951 }
952 return 1;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700953}
954
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300955void PowerRestoreController::run()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700956{
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400957 std::string powerRestorePolicyObject =
958 "/xyz/openbmc_project/control/host" + node + "/power_restore_policy";
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300959 powerRestorePolicyLog();
960 // this list only needs to be created once
961 if (matches.empty())
962 {
963 matches.emplace_back(
964 *conn,
965 match_rules::interfacesAdded() +
966 match_rules::argNpath(0, powerRestorePolicyObject) +
967 match_rules::sender(setingsService),
968 powerRestoreConfigHandler, this);
969#ifdef USE_ACBOOT
970 matches.emplace_back(*conn,
971 match_rules::interfacesAdded() +
972 match_rules::argNpath(0, powerACBootObject) +
973 match_rules::sender(setingsService),
974 powerRestoreConfigHandler, this);
975 matches.emplace_back(*conn,
976 match_rules::propertiesChanged(powerACBootObject,
977 powerACBootIface) +
978 match_rules::sender(setingsService),
979 powerRestoreConfigHandler, this);
980#endif // USE_ACBOOT
981 }
982
983 // Check if it's already on DBus
984 conn->async_method_call(
985 [this](boost::system::error_code ec,
986 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400987 if (ec)
988 {
989 return;
990 }
991 setProperties(properties);
992 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300993 setingsService, powerRestorePolicyObject,
994 "org.freedesktop.DBus.Properties", "GetAll", powerRestorePolicyIface);
995
996#ifdef USE_ACBOOT
997 // Check if it's already on DBus
998 conn->async_method_call(
999 [this](boost::system::error_code ec,
1000 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001001 if (ec)
1002 {
1003 return;
1004 }
1005 setProperties(properties);
1006 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001007 setingsService, powerACBootObject, "org.freedesktop.DBus.Properties",
1008 "GetAll", powerACBootIface);
1009#endif
1010}
1011
1012void PowerRestoreController::setProperties(const dbusPropertiesList& props)
1013{
1014 for (auto& [property, propValue] : props)
1015 {
1016 if (property == "PowerRestorePolicy")
1017 {
1018 const std::string* value = std::get_if<std::string>(&propValue);
1019 if (value == nullptr)
1020 {
1021 lg2::error("Unable to read Power Restore Policy");
1022 continue;
1023 }
1024 powerRestorePolicy = *value;
1025 }
1026 else if (property == "PowerRestoreDelay")
1027 {
1028 const uint64_t* value = std::get_if<uint64_t>(&propValue);
1029 if (value == nullptr)
1030 {
1031 lg2::error("Unable to read Power Restore Delay");
1032 continue;
1033 }
1034 powerRestoreDelay = *value / 1000000; // usec to sec
1035 }
1036#ifdef USE_ACBOOT
1037 else if (property == "ACBoot")
1038 {
1039 const std::string* value = std::get_if<std::string>(&propValue);
1040 if (value == nullptr)
1041 {
1042 lg2::error("Unable to read AC Boot status");
1043 continue;
1044 }
1045 acBoot = *value;
1046 }
1047#endif // USE_ACBOOT
1048 }
1049 invokeIfReady();
1050}
1051
1052void PowerRestoreController::invokeIfReady()
1053{
1054 if ((powerRestorePolicy.empty()) || (powerRestoreDelay < 0))
1055 {
1056 return;
1057 }
1058#ifdef USE_ACBOOT
1059 if (acBoot.empty() || acBoot == "Unknown")
1060 {
1061 return;
1062 }
1063#endif
1064
1065 matches.clear();
1066 if (!timerFired)
1067 {
1068 // Calculate the delay from now to meet the requested delay
1069 // Subtract the approximate uboot time
1070 static constexpr const int ubootSeconds = 20;
1071 int delay = powerRestoreDelay - ubootSeconds;
1072 // Subtract the time since boot
1073 struct sysinfo info = {};
1074 if (sysinfo(&info) == 0)
1075 {
1076 delay -= info.uptime;
1077 }
1078
1079 if (delay > 0)
1080 {
1081 powerRestoreTimer.expires_after(std::chrono::seconds(delay));
1082 lg2::info("Power Restore delay of {DELAY} seconds started", "DELAY",
1083 delay);
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001084 powerRestoreTimer.async_wait([this](const boost::system::error_code
1085 ec) {
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001086 if (ec)
1087 {
1088 // operation_aborted is expected if timer is canceled before
1089 // completion.
1090 if (ec == boost::asio::error::operation_aborted)
1091 {
1092 return;
1093 }
1094 lg2::error(
1095 "power restore policy async_wait failed: {ERROR_MSG}",
1096 "ERROR_MSG", ec.message());
1097 }
1098 else
1099 {
1100 lg2::info("Power Restore delay timer expired");
1101 }
1102 invoke();
1103 });
1104 timerFired = true;
1105 }
1106 else
1107 {
1108 invoke();
1109 }
1110 }
1111}
1112
1113void PowerRestoreController::invoke()
1114{
1115 // we want to run Power Restore only once
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001116 if (policyInvoked)
1117 {
1118 return;
1119 }
1120 policyInvoked = true;
1121
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001122 lg2::info("Invoking Power Restore Policy {POLICY}", "POLICY",
1123 powerRestorePolicy);
1124 if (powerRestorePolicy ==
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001125 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
1126 {
1127 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001128 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001129 }
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001130 else if (powerRestorePolicy ==
Jason M. Bills418ce112021-09-08 15:15:05 -07001131 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001132 {
1133 if (wasPowerDropped())
1134 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001135 lg2::info("Power was dropped, restoring Host On state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001136 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001137 setRestartCauseProperty(
1138 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001139 }
1140 else
1141 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001142 lg2::info("No power drop, restoring Host Off state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001143 }
1144 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -07001145 // We're done with the previous power state for the restore policy, so store
1146 // the current state
1147 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001148}
1149
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001150bool PowerRestoreController::wasPowerDropped()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001151{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001152 std::string state = appState.get(PersistentState::Params::PowerState);
1153 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001154}
1155
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001156static void waitForGPIOEvent(
1157 const std::string& name, const std::function<void(bool)>& eventHandler,
1158 gpiod::line& line, boost::asio::posix::stream_descriptor& event)
Zev Weiss676ef2c2021-09-02 21:54:02 -05001159{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001160 event.async_wait(
1161 boost::asio::posix::stream_descriptor::wait_read,
1162 [&name, eventHandler, &line,
1163 &event](const boost::system::error_code ec) {
1164 if (ec)
1165 {
1166 lg2::error("{GPIO_NAME} fd handler error: {ERROR_MSG}",
1167 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1168 // TODO: throw here to force power-control to
1169 // restart?
1170 return;
1171 }
1172 gpiod::line_event line_event = line.event_read();
1173 eventHandler(line_event.event_type ==
1174 gpiod::line_event::RISING_EDGE);
1175 waitForGPIOEvent(name, eventHandler, line, event);
1176 });
Zev Weiss676ef2c2021-09-02 21:54:02 -05001177}
1178
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001179static bool requestGPIOEvents(
Zev Weiss676ef2c2021-09-02 21:54:02 -05001180 const std::string& name, const std::function<void(bool)>& handler,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001181 gpiod::line& gpioLine,
1182 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1183{
1184 // Find the GPIO line
1185 gpioLine = gpiod::find_line(name);
1186 if (!gpioLine)
1187 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001188 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001189 return false;
1190 }
1191
1192 try
1193 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001194 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001195 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001196 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001197 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001198 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}",
1199 "GPIO_NAME", name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001200 return false;
1201 }
1202
1203 int gpioLineFd = gpioLine.event_get_fd();
1204 if (gpioLineFd < 0)
1205 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001206 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001207 return false;
1208 }
1209
1210 gpioEventDescriptor.assign(gpioLineFd);
1211
Zev Weiss676ef2c2021-09-02 21:54:02 -05001212 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001213 return true;
1214}
1215
1216static bool setGPIOOutput(const std::string& name, const int value,
1217 gpiod::line& gpioLine)
1218{
1219 // Find the GPIO line
1220 gpioLine = gpiod::find_line(name);
1221 if (!gpioLine)
1222 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001223 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001224 return false;
1225 }
1226
1227 // Request GPIO output to specified value
1228 try
1229 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001230 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}},
1231 value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001232 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001233 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001234 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001235 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
1236 name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001237 return false;
1238 }
1239
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001240 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1241 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001242 return true;
1243}
1244
1245static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1246 const std::string& name, const int value,
1247 const int durationMs)
1248{
1249 // Set the masked GPIO line to the specified value
1250 maskedGPIOLine.set_value(value);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001251 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1252 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001253 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001254 gpioAssertTimer.async_wait(
1255 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001256 // Set the masked GPIO line back to the opposite value
1257 maskedGPIOLine.set_value(!value);
1258 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1259 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001260 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001261 // operation_aborted is expected if timer is canceled before
1262 // completion.
1263 if (ec != boost::asio::error::operation_aborted)
1264 {
1265 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1266 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1267 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001268 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001269 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001270 return 0;
1271}
1272
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001273static int setGPIOOutputForMs(const ConfigData& config, const int value,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001274 const int durationMs)
1275{
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001276 // If the requested GPIO is masked, use the mask line to set the output
1277 if (powerButtonMask && config.lineName == powerOutConfig.lineName)
1278 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001279 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value,
1280 durationMs);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001281 }
1282 if (resetButtonMask && config.lineName == resetOutConfig.lineName)
1283 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001284 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value,
1285 durationMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001286 }
1287
1288 // No mask set, so request and set the GPIO normally
1289 gpiod::line gpioLine;
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001290 if (!setGPIOOutput(config.lineName, value, gpioLine))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001291 {
1292 return -1;
1293 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001294 const std::string name = config.lineName;
Jean-Marie Verdun2c495cf2021-09-17 21:42:23 -04001295
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001296 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001297 gpioAssertTimer.async_wait(
1298 [gpioLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001299 // Set the GPIO line back to the opposite value
1300 gpioLine.set_value(!value);
1301 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1302 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001303 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001304 // operation_aborted is expected if timer is canceled before
1305 // completion.
1306 if (ec != boost::asio::error::operation_aborted)
1307 {
1308 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1309 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1310 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001311 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001312 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001313 return 0;
1314}
1315
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001316static int assertGPIOForMs(const ConfigData& config, const int durationMs)
1317{
1318 return setGPIOOutputForMs(config, config.polarity, durationMs);
1319}
1320
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001321static void powerOn()
1322{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001323 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001324}
Naveen Moses117c34e2021-05-26 20:10:51 +05301325#ifdef CHASSIS_SYSTEM_RESET
1326static int slotPowerOn()
1327{
1328 if (power_control::slotPowerState != power_control::SlotPowerState::on)
1329 {
Naveen Moses117c34e2021-05-26 20:10:51 +05301330 slotPowerLine.set_value(1);
1331
1332 if (slotPowerLine.get_value() > 0)
1333 {
1334 setSlotPowerState(SlotPowerState::on);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001335 lg2::info("Slot Power is switched On\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301336 }
1337 else
1338 {
1339 return -1;
1340 }
1341 }
1342 else
1343 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001344 lg2::info("Slot Power is already in 'On' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301345 return -1;
1346 }
1347 return 0;
1348}
1349static int slotPowerOff()
1350{
1351 if (power_control::slotPowerState != power_control::SlotPowerState::off)
1352 {
1353 slotPowerLine.set_value(0);
1354
1355 if (!(slotPowerLine.get_value() > 0))
1356 {
1357 setSlotPowerState(SlotPowerState::off);
1358 setPowerState(PowerState::off);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001359 lg2::info("Slot Power is switched Off\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301360 }
1361 else
1362 {
1363 return -1;
1364 }
1365 }
1366 else
1367 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001368 lg2::info("Slot Power is already in 'Off' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301369 return -1;
1370 }
1371 return 0;
1372}
1373static void slotPowerCycle()
1374{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001375 lg2::info("Slot Power Cycle started\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301376 slotPowerOff();
1377 slotPowerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001378 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"]));
Naveen Moses117c34e2021-05-26 20:10:51 +05301379 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) {
1380 if (ec)
1381 {
1382 if (ec != boost::asio::error::operation_aborted)
1383 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001384 lg2::error(
1385 "Slot Power cycle timer async_wait failed: {ERROR_MSG}",
1386 "ERROR_MSG", ec.message());
Naveen Moses117c34e2021-05-26 20:10:51 +05301387 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001388 lg2::info("Slot Power cycle timer canceled\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301389 return;
1390 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001391 lg2::info("Slot Power cycle timer completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301392 slotPowerOn();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001393 lg2::info("Slot Power Cycle Completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301394 });
1395}
1396#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001397static void gracefulPowerOff()
1398{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001399 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001400}
1401
1402static void forcePowerOff()
1403{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001404 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001405 {
1406 return;
1407 }
1408
Jason M. Billsc6961b62021-10-21 14:08:02 -07001409 // If the force off timer expires, then the power-button override failed
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001410 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1411 if (ec)
1412 {
1413 // operation_aborted is expected if timer is canceled before
1414 // completion.
1415 if (ec != boost::asio::error::operation_aborted)
1416 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001417 lg2::error("Force power off async_wait failed: {ERROR_MSG}",
1418 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001419 }
1420 return;
1421 }
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001422
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001423 lg2::error("Power-button override failed. Not sure what to do now.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001424 });
1425}
1426
1427static void reset()
1428{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001429 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001430}
1431
1432static void gracefulPowerOffTimerStart()
1433{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001434 lg2::info("Graceful power-off timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001435 gracefulPowerOffTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001436 std::chrono::seconds(TimerMap["GracefulPowerOffS"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001437 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1438 if (ec)
1439 {
1440 // operation_aborted is expected if timer is canceled before
1441 // completion.
1442 if (ec != boost::asio::error::operation_aborted)
1443 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001444 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}",
1445 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001446 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001447 lg2::info("Graceful power-off timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001448 return;
1449 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001450 lg2::info("Graceful power-off timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001451 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1452 });
1453}
1454
1455static void powerCycleTimerStart()
1456{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001457 lg2::info("Power-cycle timer started");
Priyatharshan P70120512020-09-16 18:47:20 +05301458 powerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001459 std::chrono::milliseconds(TimerMap["PowerCycleMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001460 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1461 if (ec)
1462 {
1463 // operation_aborted is expected if timer is canceled before
1464 // completion.
1465 if (ec != boost::asio::error::operation_aborted)
1466 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001467 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}",
1468 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001469 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001470 lg2::info("Power-cycle timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001471 return;
1472 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001473 lg2::info("Power-cycle timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001474 sendPowerControlEvent(Event::powerCycleTimerExpired);
1475 });
1476}
1477
Jason M. Bills35471132025-05-06 12:26:10 -07001478static void powerOKWatchdogTimerStart()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001479{
Jason M. Bills35471132025-05-06 12:26:10 -07001480 lg2::info("power OK watchdog timer started");
1481 powerOKWatchdogTimer.expires_after(
1482 std::chrono::milliseconds(TimerMap["PowerOKWatchdogMs"]));
1483 powerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) {
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001484 if (ec)
1485 {
1486 // operation_aborted is expected if timer is canceled before
1487 // completion.
1488 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001489 {
Jason M. Bills35471132025-05-06 12:26:10 -07001490 lg2::error("power OK watchdog async_wait failed: {ERROR_MSG}",
1491 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001492 }
Jason M. Bills35471132025-05-06 12:26:10 -07001493 lg2::info("power OK watchdog timer canceled");
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001494 return;
1495 }
Jason M. Bills35471132025-05-06 12:26:10 -07001496 lg2::info("power OK watchdog timer expired");
1497 sendPowerControlEvent(Event::powerOKWatchdogTimerExpired);
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001498 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001499}
1500
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001501static void warmResetCheckTimerStart()
1502{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001503 lg2::info("Warm reset check timer started");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001504 warmResetCheckTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001505 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"]));
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001506 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1507 if (ec)
1508 {
1509 // operation_aborted is expected if timer is canceled before
1510 // completion.
1511 if (ec != boost::asio::error::operation_aborted)
1512 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001513 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}",
1514 "ERROR_MSG", ec.message());
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001515 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001516 lg2::info("Warm reset check timer canceled");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001517 return;
1518 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001519 lg2::info("Warm reset check timer completed");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001520 sendPowerControlEvent(Event::warmResetDetected);
1521 });
1522}
1523
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001524static void pohCounterTimerStart()
1525{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001526 lg2::info("POH timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001527 // Set the time-out as 1 hour, to align with POH command in ipmid
1528 pohCounterTimer.expires_after(std::chrono::hours(1));
1529 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1530 if (ec)
1531 {
1532 // operation_aborted is expected if timer is canceled before
1533 // completion.
1534 if (ec != boost::asio::error::operation_aborted)
1535 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001536 lg2::error("POH timer async_wait failed: {ERROR_MSG}",
1537 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001538 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001539 lg2::info("POH timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001540 return;
1541 }
1542
1543 if (getHostState(powerState) !=
1544 "xyz.openbmc_project.State.Host.HostState.Running")
1545 {
1546 return;
1547 }
1548
1549 conn->async_method_call(
1550 [](boost::system::error_code ec,
1551 const std::variant<uint32_t>& pohCounterProperty) {
1552 if (ec)
1553 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001554 lg2::error("error getting poh counter");
1555 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001556 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001557 const uint32_t* pohCounter =
1558 std::get_if<uint32_t>(&pohCounterProperty);
1559 if (pohCounter == nullptr)
1560 {
1561 lg2::error("unable to read poh counter");
1562 return;
1563 }
1564
1565 conn->async_method_call(
1566 [](boost::system::error_code ec) {
1567 if (ec)
1568 {
1569 lg2::error("failed to set poh counter");
1570 }
1571 },
1572 "xyz.openbmc_project.Settings",
1573 "/xyz/openbmc_project/state/chassis0",
1574 "org.freedesktop.DBus.Properties", "Set",
1575 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1576 std::variant<uint32_t>(*pohCounter + 1));
Patrick Williamsd394c882023-10-20 11:18:44 -05001577 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001578 "xyz.openbmc_project.Settings",
1579 "/xyz/openbmc_project/state/chassis0",
1580 "org.freedesktop.DBus.Properties", "Get",
1581 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1582
1583 pohCounterTimerStart();
1584 });
1585}
1586
1587static void currentHostStateMonitor()
1588{
Yong Li8d660212019-12-27 10:18:10 +08001589 if (getHostState(powerState) ==
1590 "xyz.openbmc_project.State.Host.HostState.Running")
1591 {
1592 pohCounterTimerStart();
1593 // Clear the restart cause set for the next restart
1594 clearRestartCause();
1595 }
1596 else
1597 {
1598 pohCounterTimer.cancel();
1599 // Set the restart cause set for this restart
1600 setRestartCause();
1601 }
1602
Jayanth Othayoth857c5582025-06-12 11:17:51 -05001603 std::string objectPath = "/xyz/openbmc_project/state/host" + node;
1604
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001605 static auto match = sdbusplus::bus::match_t(
1606 *conn,
1607 "type='signal',member='PropertiesChanged', "
1608 "interface='org.freedesktop.DBus.Properties', "
Jayanth Othayoth857c5582025-06-12 11:17:51 -05001609 "path='" +
1610 objectPath +
1611 "',"
1612 "arg0='xyz.openbmc_project.State.Host'",
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001613 [](sdbusplus::message_t& message) {
1614 std::string intfName;
1615 std::map<std::string, std::variant<std::string>> properties;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001616
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001617 try
1618 {
1619 message.read(intfName, properties);
1620 }
1621 catch (const std::exception& e)
1622 {
1623 lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1624 return;
1625 }
1626 if (properties.empty())
1627 {
1628 lg2::error("ERROR: Empty PropertiesChanged signal received");
1629 return;
1630 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001631
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001632 // We only want to check for CurrentHostState
1633 if (properties.begin()->first != "CurrentHostState")
1634 {
1635 return;
1636 }
1637 std::string* currentHostState =
1638 std::get_if<std::string>(&(properties.begin()->second));
1639 if (currentHostState == nullptr)
1640 {
1641 lg2::error("{PROPERTY} property invalid", "PROPERTY",
1642 properties.begin()->first);
1643 return;
1644 }
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001645
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001646 if (*currentHostState ==
1647 "xyz.openbmc_project.State.Host.HostState.Running")
1648 {
1649 pohCounterTimerStart();
1650 // Clear the restart cause set for the next restart
1651 clearRestartCause();
1652 sd_journal_send("MESSAGE=Host system DC power is on",
1653 "PRIORITY=%i", LOG_INFO,
1654 "REDFISH_MESSAGE_ID=%s",
1655 "OpenBMC.0.1.DCPowerOn", NULL);
1656 }
1657 else
1658 {
1659 pohCounterTimer.cancel();
1660 // POST_COMPLETE GPIO event is not working in some platforms
1661 // when power state is changed to OFF. This resulted in
1662 // 'OperatingSystemState' to stay at 'Standby', even though
1663 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1664 // if HostState is trurned to OFF.
1665 setOperatingSystemState(OperatingSystemStateStage::Inactive);
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301666
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001667 // Set the restart cause set for this restart
1668 setRestartCause();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001669#ifdef USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001670 resetACBootProperty();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001671#endif // USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001672 sd_journal_send("MESSAGE=Host system DC power is off",
1673 "PRIORITY=%i", LOG_INFO,
1674 "REDFISH_MESSAGE_ID=%s",
1675 "OpenBMC.0.1.DCPowerOff", NULL);
1676 }
1677 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001678}
1679
1680static void sioPowerGoodWatchdogTimerStart()
1681{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001682 lg2::info("SIO power good watchdog timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001683 sioPowerGoodWatchdogTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001684 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001685 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code
1686 ec) {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001687 if (ec)
1688 {
1689 // operation_aborted is expected if timer is canceled before
1690 // completion.
1691 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001692 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001693 lg2::error(
1694 "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1695 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001696 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001697 lg2::info("SIO power good watchdog timer canceled");
1698 return;
1699 }
1700 lg2::info("SIO power good watchdog timer completed");
1701 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1702 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001703}
1704
1705static void powerStateOn(const Event event)
1706{
1707 logEvent(__FUNCTION__, event);
1708 switch (event)
1709 {
Jason M. Bills35471132025-05-06 12:26:10 -07001710 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001711 setPowerState(PowerState::off);
1712 // DC power is unexpectedly lost, beep
1713 beep(beepPowerFail);
1714 break;
1715 case Event::sioS5Assert:
1716 setPowerState(PowerState::transitionToOff);
Matt Simmering58e379d2022-09-23 14:45:50 -07001717#if IGNORE_SOFT_RESETS_DURING_POST
1718 // Only recognize soft resets once host gets past POST COMPLETE
1719 if (operatingSystemState != OperatingSystemStateStage::Standby)
1720 {
1721 ignoreNextSoftReset = true;
1722 }
1723#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001724 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001725 break;
Jason M. Billsfb957332021-01-28 13:18:46 -08001726#if USE_PLT_RST
1727 case Event::pltRstAssert:
1728#else
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001729 case Event::postCompleteDeAssert:
Jason M. Billsfb957332021-01-28 13:18:46 -08001730#endif
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001731 setPowerState(PowerState::checkForWarmReset);
Matt Simmering58e379d2022-09-23 14:45:50 -07001732#if IGNORE_SOFT_RESETS_DURING_POST
1733 // Only recognize soft resets once host gets past POST COMPLETE
1734 if (operatingSystemState != OperatingSystemStateStage::Standby)
1735 {
1736 ignoreNextSoftReset = true;
1737 }
1738#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001739 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001740 warmResetCheckTimerStart();
1741 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001742 case Event::powerButtonPressed:
1743 setPowerState(PowerState::gracefulTransitionToOff);
1744 gracefulPowerOffTimerStart();
1745 break;
1746 case Event::powerOffRequest:
1747 setPowerState(PowerState::transitionToOff);
1748 forcePowerOff();
1749 break;
1750 case Event::gracefulPowerOffRequest:
1751 setPowerState(PowerState::gracefulTransitionToOff);
1752 gracefulPowerOffTimerStart();
1753 gracefulPowerOff();
1754 break;
1755 case Event::powerCycleRequest:
1756 setPowerState(PowerState::transitionToCycleOff);
1757 forcePowerOff();
1758 break;
1759 case Event::gracefulPowerCycleRequest:
1760 setPowerState(PowerState::gracefulTransitionToCycleOff);
1761 gracefulPowerOffTimerStart();
1762 gracefulPowerOff();
1763 break;
Jayanth Othayothdc0bab92024-02-07 07:24:35 -06001764 case Event::resetButtonPressed:
1765 setPowerState(PowerState::checkForWarmReset);
1766 warmResetCheckTimerStart();
1767 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001768 case Event::resetRequest:
1769 reset();
1770 break;
1771 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001772 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001773 break;
1774 }
1775}
1776
Jason M. Bills35471132025-05-06 12:26:10 -07001777static void powerStateWaitForPowerOK(const Event event)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001778{
1779 logEvent(__FUNCTION__, event);
1780 switch (event)
1781 {
Jason M. Bills35471132025-05-06 12:26:10 -07001782 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301783 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001784 // Cancel any GPIO assertions held during the transition
1785 gpioAssertTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001786 powerOKWatchdogTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301787 if (sioEnabled == true)
1788 {
1789 sioPowerGoodWatchdogTimerStart();
1790 setPowerState(PowerState::waitForSIOPowerGood);
1791 }
1792 else
1793 {
1794 setPowerState(PowerState::on);
1795 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001796 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301797 }
Jason M. Bills35471132025-05-06 12:26:10 -07001798 case Event::powerOKWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001799 setPowerState(PowerState::off);
Jason M. Bills35471132025-05-06 12:26:10 -07001800 powerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001801 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001802 case Event::sioPowerGoodAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001803 powerOKWatchdogTimer.cancel();
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001804 setPowerState(PowerState::on);
1805 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001806 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001807 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001808 break;
1809 }
1810}
1811
1812static void powerStateWaitForSIOPowerGood(const Event event)
1813{
1814 logEvent(__FUNCTION__, event);
1815 switch (event)
1816 {
1817 case Event::sioPowerGoodAssert:
1818 sioPowerGoodWatchdogTimer.cancel();
1819 setPowerState(PowerState::on);
1820 break;
1821 case Event::sioPowerGoodWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001822 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001823 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001824 break;
1825 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001826 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001827 break;
1828 }
1829}
1830
1831static void powerStateOff(const Event event)
1832{
1833 logEvent(__FUNCTION__, event);
1834 switch (event)
1835 {
Jason M. Bills35471132025-05-06 12:26:10 -07001836 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301837 {
1838 if (sioEnabled == true)
1839 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001840 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301841 setPowerState(PowerState::waitForSIOPowerGood);
1842 }
1843 else
1844 {
1845 setPowerState(PowerState::on);
1846 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001847 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301848 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001849 case Event::sioS5DeAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001850 powerOKWatchdogTimerStart();
1851 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001852 break;
Jason M. Bills273d7892020-06-17 14:46:57 -07001853 case Event::sioPowerGoodAssert:
1854 setPowerState(PowerState::on);
1855 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001856 case Event::powerButtonPressed:
Jason M. Bills35471132025-05-06 12:26:10 -07001857 powerOKWatchdogTimerStart();
1858 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001859 break;
1860 case Event::powerOnRequest:
Jason M. Bills35471132025-05-06 12:26:10 -07001861 powerOKWatchdogTimerStart();
1862 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001863 powerOn();
1864 break;
1865 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001866 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001867 break;
1868 }
1869}
1870
1871static void powerStateTransitionToOff(const Event event)
1872{
1873 logEvent(__FUNCTION__, event);
1874 switch (event)
1875 {
Jason M. Bills35471132025-05-06 12:26:10 -07001876 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001877 // Cancel any GPIO assertions held during the transition
1878 gpioAssertTimer.cancel();
1879 setPowerState(PowerState::off);
1880 break;
1881 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001882 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001883 break;
1884 }
1885}
1886
1887static void powerStateGracefulTransitionToOff(const Event event)
1888{
1889 logEvent(__FUNCTION__, event);
1890 switch (event)
1891 {
Jason M. Bills35471132025-05-06 12:26:10 -07001892 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001893 gracefulPowerOffTimer.cancel();
1894 setPowerState(PowerState::off);
1895 break;
1896 case Event::gracefulPowerOffTimerExpired:
1897 setPowerState(PowerState::on);
1898 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001899 case Event::powerOffRequest:
1900 gracefulPowerOffTimer.cancel();
1901 setPowerState(PowerState::transitionToOff);
1902 forcePowerOff();
1903 break;
1904 case Event::powerCycleRequest:
1905 gracefulPowerOffTimer.cancel();
1906 setPowerState(PowerState::transitionToCycleOff);
1907 forcePowerOff();
1908 break;
1909 case Event::resetRequest:
1910 gracefulPowerOffTimer.cancel();
1911 setPowerState(PowerState::on);
1912 reset();
1913 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001914 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001915 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001916 break;
1917 }
1918}
1919
1920static void powerStateCycleOff(const Event event)
1921{
1922 logEvent(__FUNCTION__, event);
1923 switch (event)
1924 {
Jason M. Bills35471132025-05-06 12:26:10 -07001925 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301926 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001927 powerCycleTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301928 if (sioEnabled == true)
1929 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001930 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301931 setPowerState(PowerState::waitForSIOPowerGood);
1932 }
1933 else
1934 {
1935 setPowerState(PowerState::on);
1936 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001937 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301938 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001939 case Event::sioS5DeAssert:
1940 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001941 powerOKWatchdogTimerStart();
1942 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001943 break;
1944 case Event::powerButtonPressed:
1945 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001946 powerOKWatchdogTimerStart();
1947 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001948 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001949 case Event::powerCycleTimerExpired:
Jason M. Bills35471132025-05-06 12:26:10 -07001950 powerOKWatchdogTimerStart();
1951 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001952 powerOn();
1953 break;
1954 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001955 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001956 break;
1957 }
1958}
1959
1960static void powerStateTransitionToCycleOff(const Event event)
1961{
1962 logEvent(__FUNCTION__, event);
1963 switch (event)
1964 {
Jason M. Bills35471132025-05-06 12:26:10 -07001965 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001966 // Cancel any GPIO assertions held during the transition
1967 gpioAssertTimer.cancel();
1968 setPowerState(PowerState::cycleOff);
1969 powerCycleTimerStart();
1970 break;
1971 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001972 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001973 break;
1974 }
1975}
1976
1977static void powerStateGracefulTransitionToCycleOff(const Event event)
1978{
1979 logEvent(__FUNCTION__, event);
1980 switch (event)
1981 {
Jason M. Bills35471132025-05-06 12:26:10 -07001982 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001983 gracefulPowerOffTimer.cancel();
1984 setPowerState(PowerState::cycleOff);
1985 powerCycleTimerStart();
1986 break;
1987 case Event::gracefulPowerOffTimerExpired:
1988 setPowerState(PowerState::on);
1989 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001990 case Event::powerOffRequest:
1991 gracefulPowerOffTimer.cancel();
1992 setPowerState(PowerState::transitionToOff);
1993 forcePowerOff();
1994 break;
1995 case Event::powerCycleRequest:
1996 gracefulPowerOffTimer.cancel();
1997 setPowerState(PowerState::transitionToCycleOff);
1998 forcePowerOff();
1999 break;
2000 case Event::resetRequest:
2001 gracefulPowerOffTimer.cancel();
2002 setPowerState(PowerState::on);
2003 reset();
2004 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002005 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002006 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002007 break;
2008 }
2009}
2010
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002011static void powerStateCheckForWarmReset(const Event event)
2012{
2013 logEvent(__FUNCTION__, event);
2014 switch (event)
2015 {
2016 case Event::sioS5Assert:
2017 warmResetCheckTimer.cancel();
2018 setPowerState(PowerState::transitionToOff);
2019 break;
2020 case Event::warmResetDetected:
2021 setPowerState(PowerState::on);
2022 break;
Jason M. Bills35471132025-05-06 12:26:10 -07002023 case Event::powerOKDeAssert:
P.K. Lee344dae82019-11-27 16:35:05 +08002024 warmResetCheckTimer.cancel();
2025 setPowerState(PowerState::off);
2026 // DC power is unexpectedly lost, beep
2027 beep(beepPowerFail);
2028 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002029 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002030 lg2::info("No action taken.");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002031 break;
2032 }
2033}
2034
Jason M. Bills35471132025-05-06 12:26:10 -07002035static void powerOKHandler(bool state)
Zev Weiss584aa132021-09-02 19:21:52 -05002036{
Zev Weissedc86f32024-05-07 01:44:33 +00002037 Event powerControlEvent = (state == powerOkConfig.polarity)
Jason M. Bills35471132025-05-06 12:26:10 -07002038 ? Event::powerOKAssert
2039 : Event::powerOKDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002040 sendPowerControlEvent(powerControlEvent);
2041}
2042
Zev Weiss584aa132021-09-02 19:21:52 -05002043static void sioPowerGoodHandler(bool state)
2044{
Zev Weissedc86f32024-05-07 01:44:33 +00002045 Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2046 ? Event::sioPowerGoodAssert
2047 : Event::sioPowerGoodDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002048 sendPowerControlEvent(powerControlEvent);
2049}
2050
Zev Weiss584aa132021-09-02 19:21:52 -05002051static void sioOnControlHandler(bool state)
2052{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002053 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2054 static_cast<int>(state));
Zev Weiss584aa132021-09-02 19:21:52 -05002055}
2056
Zev Weiss584aa132021-09-02 19:21:52 -05002057static void sioS5Handler(bool state)
2058{
Zev Weissedc86f32024-05-07 01:44:33 +00002059 Event powerControlEvent = (state == sioS5Config.polarity)
2060 ? Event::sioS5Assert
2061 : Event::sioS5DeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002062 sendPowerControlEvent(powerControlEvent);
2063}
2064
Zev Weiss584aa132021-09-02 19:21:52 -05002065static void powerButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002066{
Zev Weissedc86f32024-05-07 01:44:33 +00002067 bool asserted = state == powerButtonConfig.polarity;
2068 powerButtonIface->set_property("ButtonPressed", asserted);
2069 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002070 {
2071 powerButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002072 if (!powerButtonMask)
2073 {
2074 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002075 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002076 }
2077 else
2078 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002079 lg2::info("power button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002080 }
2081 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002082#if USE_BUTTON_PASSTHROUGH
2083 gpiod::line gpioLine;
2084 bool outputState =
2085 asserted ? powerOutConfig.polarity : (!powerOutConfig.polarity);
2086 if (!setGPIOOutput(powerOutConfig.lineName, outputState, gpioLine))
2087 {
2088 lg2::error("{GPIO_NAME} power button passthrough failed", "GPIO_NAME",
2089 powerOutConfig.lineName);
2090 }
2091#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002092}
2093
Zev Weiss584aa132021-09-02 19:21:52 -05002094static void resetButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002095{
Zev Weissedc86f32024-05-07 01:44:33 +00002096 bool asserted = state == resetButtonConfig.polarity;
2097 resetButtonIface->set_property("ButtonPressed", asserted);
2098 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002099 {
2100 resetButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002101 if (!resetButtonMask)
2102 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002103 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002104 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002105 }
2106 else
2107 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002108 lg2::info("reset button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002109 }
2110 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002111#if USE_BUTTON_PASSTHROUGH
2112 gpiod::line gpioLine;
2113 bool outputState =
2114 asserted ? resetOutConfig.polarity : (!resetOutConfig.polarity);
2115 if (!setGPIOOutput(resetOutConfig.lineName, outputState, gpioLine))
2116 {
2117 lg2::error("{GPIO_NAME} reset button passthrough failed", "GPIO_NAME",
2118 resetOutConfig.lineName);
2119 }
2120#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002121}
2122
Vijay Khemka04175c22020-10-09 14:28:11 -07002123#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002124static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2125static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2126static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2127static constexpr auto systemTargetName = "chassis-system-reset.target";
2128
2129void systemReset()
2130{
2131 conn->async_method_call(
2132 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002133 if (ec)
2134 {
2135 lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2136 ec.message());
2137 }
2138 },
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002139 systemdBusname, systemdPath, systemdInterface, "StartUnit",
2140 systemTargetName, "replace");
2141}
Vijay Khemka04175c22020-10-09 14:28:11 -07002142#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002143
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002144static void nmiSetEnableProperty(bool value)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002145{
2146 conn->async_method_call(
2147 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002148 if (ec)
2149 {
2150 lg2::error("failed to set NMI source");
2151 }
2152 },
Chen Yugang303bd582019-11-01 08:45:06 +08002153 "xyz.openbmc_project.Settings",
2154 "/xyz/openbmc_project/Chassis/Control/NMISource",
2155 "org.freedesktop.DBus.Properties", "Set",
2156 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2157 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002158}
2159
2160static void nmiReset(void)
2161{
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002162 const static constexpr int nmiOutPulseTimeMs = 200;
2163
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002164 lg2::info("NMI out action");
Jian Zhang461a1662022-09-22 11:29:01 +08002165 nmiOutLine.set_value(nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002166 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
Jian Zhang461a1662022-09-22 11:29:01 +08002167 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002168 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2169 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2170 // restore the NMI_OUT GPIO line back to the opposite value
Jian Zhang461a1662022-09-22 11:29:01 +08002171 nmiOutLine.set_value(!nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002172 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002173 if (ec)
2174 {
2175 // operation_aborted is expected if timer is canceled before
2176 // completion.
2177 if (ec != boost::asio::error::operation_aborted)
2178 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002179 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2180 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2181 ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002182 }
2183 }
2184 });
2185 // log to redfish
2186 nmiDiagIntLog();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002187 lg2::info("NMI out action completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002188 // reset Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002189 nmiSetEnableProperty(false);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002190}
2191
2192static void nmiSourcePropertyMonitor(void)
2193{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002194 lg2::info("NMI Source Property Monitor");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002195
Patrick Williams439b9c32022-07-22 19:26:53 -05002196 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2197 std::make_unique<sdbusplus::bus::match_t>(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002198 *conn,
2199 "type='signal',interface='org.freedesktop.DBus.Properties',"
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002200 "member='PropertiesChanged',"
2201 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
Patrick Williams439b9c32022-07-22 19:26:53 -05002202 [](sdbusplus::message_t& msg) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002203 std::string interfaceName;
2204 boost::container::flat_map<std::string,
2205 std::variant<bool, std::string>>
2206 propertiesChanged;
2207 std::string state;
2208 bool value = true;
2209 try
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002210 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002211 msg.read(interfaceName, propertiesChanged);
2212 if (propertiesChanged.begin()->first == "Enabled")
2213 {
2214 value =
2215 std::get<bool>(propertiesChanged.begin()->second);
2216 lg2::info(
2217 "NMI Enabled propertiesChanged value: {VALUE}",
2218 "VALUE", value);
2219 nmiEnabled = value;
2220 if (nmiEnabled)
2221 {
2222 nmiReset();
2223 }
2224 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002225 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002226 catch (const std::exception& e)
2227 {
2228 lg2::error("Unable to read NMI source: {ERROR}", "ERROR",
2229 e);
2230 return;
2231 }
2232 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002233}
2234
2235static void setNmiSource()
2236{
2237 conn->async_method_call(
2238 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002239 if (ec)
2240 {
2241 lg2::error("failed to set NMI source");
2242 }
2243 },
Chen Yugang303bd582019-11-01 08:45:06 +08002244 "xyz.openbmc_project.Settings",
2245 "/xyz/openbmc_project/Chassis/Control/NMISource",
2246 "org.freedesktop.DBus.Properties", "Set",
2247 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
Jason M. Bills418ce112021-09-08 15:15:05 -07002248 std::variant<std::string>{
Tim Lee6af569f2024-03-11 17:32:42 +08002249 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002250 // set Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002251 nmiSetEnableProperty(true);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002252}
2253
Zev Weiss584aa132021-09-02 19:21:52 -05002254static void nmiButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002255{
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002256 // Don't handle event if host not running and config doesn't force it
2257 if (!nmiWhenPoweredOff &&
2258 getHostState(powerState) !=
2259 "xyz.openbmc_project.State.Host.HostState.Running")
2260 {
2261 return;
2262 }
Zev Weissedc86f32024-05-07 01:44:33 +00002263
2264 bool asserted = state == nmiButtonConfig.polarity;
2265 nmiButtonIface->set_property("ButtonPressed", asserted);
2266 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002267 {
2268 nmiButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002269 if (nmiButtonMasked)
2270 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002271 lg2::info("NMI button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002272 }
2273 else
2274 {
2275 setNmiSource();
2276 }
2277 }
Zev Weiss584aa132021-09-02 19:21:52 -05002278}
2279
Zev Weiss584aa132021-09-02 19:21:52 -05002280static void idButtonHandler(bool state)
2281{
Zev Weissedc86f32024-05-07 01:44:33 +00002282 bool asserted = state == idButtonConfig.polarity;
2283 idButtonIface->set_property("ButtonPressed", asserted);
Zev Weiss584aa132021-09-02 19:21:52 -05002284}
2285
Jason M. Billsfb957332021-01-28 13:18:46 -08002286static void pltRstHandler(bool pltRst)
2287{
2288 if (pltRst)
2289 {
2290 sendPowerControlEvent(Event::pltRstDeAssert);
2291 }
2292 else
2293 {
2294 sendPowerControlEvent(Event::pltRstAssert);
2295 }
2296}
2297
Patrick Williams439b9c32022-07-22 19:26:53 -05002298[[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
Jason M. Billsfb957332021-01-28 13:18:46 -08002299{
2300 std::string interfaceName;
2301 boost::container::flat_map<std::string, std::variant<bool>>
2302 propertiesChanged;
Jason M. Billsfb957332021-01-28 13:18:46 -08002303 try
2304 {
2305 msg.read(interfaceName, propertiesChanged);
2306 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002307 catch (const std::exception& e)
Jason M. Billsfb957332021-01-28 13:18:46 -08002308 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002309 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
Jason M. Billsfb957332021-01-28 13:18:46 -08002310 return;
2311 }
2312 if (propertiesChanged.empty())
2313 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002314 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
Jason M. Billsfb957332021-01-28 13:18:46 -08002315 return;
2316 }
2317
2318 for (auto& [property, value] : propertiesChanged)
2319 {
2320 if (property == "ESpiPlatformReset")
2321 {
2322 bool* pltRst = std::get_if<bool>(&value);
2323 if (pltRst == nullptr)
2324 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002325 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
Jason M. Billsfb957332021-01-28 13:18:46 -08002326 return;
2327 }
2328 pltRstHandler(*pltRst);
2329 }
2330 }
2331}
2332
Zev Weiss584aa132021-09-02 19:21:52 -05002333static void postCompleteHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002334{
Zev Weissedc86f32024-05-07 01:44:33 +00002335 bool asserted = state == postCompleteConfig.polarity;
2336 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002337 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002338 sendPowerControlEvent(Event::postCompleteAssert);
Tim Lee86239182021-12-23 11:46:01 +08002339 setOperatingSystemState(OperatingSystemStateStage::Standby);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002340 }
2341 else
2342 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002343 sendPowerControlEvent(Event::postCompleteDeAssert);
Tim Lee86239182021-12-23 11:46:01 +08002344 setOperatingSystemState(OperatingSystemStateStage::Inactive);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002345 }
Zev Weiss584aa132021-09-02 19:21:52 -05002346}
2347
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302348static int loadConfigValues()
2349{
2350 const std::string configFilePath =
2351 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2352 ".json";
2353 std::ifstream configFile(configFilePath.c_str());
2354 if (!configFile.is_open())
2355 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002356 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2357 "PATH", configFilePath);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302358 return -1;
2359 }
Zev Weiss1aa08b22021-09-15 17:06:20 -05002360 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302361
Priyatharshan P70120512020-09-16 18:47:20 +05302362 if (jsonData.is_discarded())
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302363 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002364 lg2::error("Power config readings JSON parser failure");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302365 return -1;
2366 }
Priyatharshan P70120512020-09-16 18:47:20 +05302367 auto gpios = jsonData["gpio_configs"];
2368 auto timers = jsonData["timing_configs"];
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302369
Priyatharshan P70120512020-09-16 18:47:20 +05302370 ConfigData* tempGpioData;
2371
2372 for (nlohmann::json& gpioConfig : gpios)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302373 {
Priyatharshan P70120512020-09-16 18:47:20 +05302374 if (!gpioConfig.contains("Name"))
2375 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002376 lg2::error("The 'Name' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302377 return -1;
2378 }
2379
2380 // Iterate through the powersignal map to check if the gpio json config
2381 // entry is valid
2382 std::string gpioName = gpioConfig["Name"];
2383 auto signalMapIter = powerSignalMap.find(gpioName);
2384 if (signalMapIter == powerSignalMap.end())
2385 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002386 lg2::error(
2387 "{GPIO_NAME} is not a recognized power-control signal name",
2388 "GPIO_NAME", gpioName);
Priyatharshan P70120512020-09-16 18:47:20 +05302389 return -1;
2390 }
2391
2392 // assign the power signal name to the corresponding structure reference
2393 // from map then fillup the structure with coressponding json config
2394 // value
2395 tempGpioData = signalMapIter->second;
2396 tempGpioData->name = gpioName;
2397
2398 if (!gpioConfig.contains("Type"))
2399 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002400 lg2::error("The \'Type\' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302401 return -1;
2402 }
2403
2404 std::string signalType = gpioConfig["Type"];
2405 if (signalType == "GPIO")
2406 {
2407 tempGpioData->type = ConfigType::GPIO;
2408 }
2409 else if (signalType == "DBUS")
2410 {
2411 tempGpioData->type = ConfigType::DBUS;
2412 }
2413 else
2414 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002415 lg2::error("{TYPE} is not a recognized power-control signal type",
2416 "TYPE", signalType);
Priyatharshan P70120512020-09-16 18:47:20 +05302417 return -1;
2418 }
2419
2420 if (tempGpioData->type == ConfigType::GPIO)
2421 {
2422 if (gpioConfig.contains("LineName"))
2423 {
2424 tempGpioData->lineName = gpioConfig["LineName"];
2425 }
2426 else
2427 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002428 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002429 "The \'LineName\' field must be defined for GPIO configuration");
Priyatharshan P70120512020-09-16 18:47:20 +05302430 return -1;
2431 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002432 if (gpioConfig.contains("Polarity"))
2433 {
2434 std::string polarity = gpioConfig["Polarity"];
2435 if (polarity == "ActiveLow")
2436 {
2437 tempGpioData->polarity = false;
2438 }
2439 else if (polarity == "ActiveHigh")
2440 {
2441 tempGpioData->polarity = true;
2442 }
2443 else
2444 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002445 lg2::error(
2446 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2447 "POLARITY", polarity);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002448 return -1;
2449 }
2450 }
2451 else
2452 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002453 lg2::error("Polarity field not found for {GPIO_NAME}",
2454 "GPIO_NAME", tempGpioData->lineName);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002455 return -1;
2456 }
Priyatharshan P70120512020-09-16 18:47:20 +05302457 }
2458 else
2459 {
2460 // if dbus based gpio config is defined read and update the dbus
2461 // params corresponding to the gpio config instance
2462 for (auto& [key, dbusParamName] : dbusParams)
2463 {
Logananth Sundararaja4308042021-10-20 11:52:05 +05302464 if (!gpioConfig.contains(dbusParamName))
Priyatharshan P70120512020-09-16 18:47:20 +05302465 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002466 lg2::error(
2467 "The {DBUS_NAME} field must be defined for Dbus configuration ",
2468 "DBUS_NAME", dbusParamName);
Priyatharshan P70120512020-09-16 18:47:20 +05302469 return -1;
2470 }
2471 }
Logananth Sundararaja4308042021-10-20 11:52:05 +05302472 tempGpioData->dbusName =
2473 gpioConfig[dbusParams[DbusConfigType::name]];
2474 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
Priyatharshan P70120512020-09-16 18:47:20 +05302475 tempGpioData->interface =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302476 gpioConfig[dbusParams[DbusConfigType::interface]];
Priyatharshan P70120512020-09-16 18:47:20 +05302477 tempGpioData->lineName =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302478 gpioConfig[dbusParams[DbusConfigType::property]];
Zev Weissca478552024-06-11 23:45:58 +00002479
Zev Weissd603dd12024-06-19 21:50:52 +00002480 // dbus-based inputs must be active-high.
2481 tempGpioData->polarity = true;
2482
Zev Weissca478552024-06-11 23:45:58 +00002483 // MatchRegex is optional
2484 auto item = gpioConfig.find("MatchRegex");
2485 if (item != gpioConfig.end())
2486 {
2487 try
2488 {
2489 tempGpioData->matchRegex = std::regex(*item);
2490 }
2491 catch (const std::regex_error& e)
2492 {
2493 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2494 gpioName, "ERR", e.what());
2495 return -1;
2496 }
2497 }
Priyatharshan P70120512020-09-16 18:47:20 +05302498 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302499 }
2500
Priyatharshan P70120512020-09-16 18:47:20 +05302501 // read and store the timer values from json config to Timer Map
2502 for (auto& [key, timerValue] : TimerMap)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302503 {
Priyatharshan P70120512020-09-16 18:47:20 +05302504 if (timers.contains(key.c_str()))
2505 {
2506 timerValue = timers[key.c_str()];
2507 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302508 }
2509
Jonathan Doman891bbde2023-05-17 13:58:24 -07002510 // If "events_configs" key is not in json config, fallback to null
2511 auto events = jsonData.value("event_configs",
2512 nlohmann::json(nlohmann::json::value_t::null));
2513 if (events.is_object())
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002514 {
2515 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2516 }
2517
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302518 return 0;
2519}
Zev Weissa8f116a2021-09-01 21:08:30 -05002520
Zev Weissca478552024-06-11 23:45:58 +00002521template <typename T>
Patrick Williamsb84e7892025-02-01 08:22:06 -05002522static std::optional<T> getMessageValue(sdbusplus::message_t& msg,
2523 const std::string& name)
Zev Weissa8f116a2021-09-01 21:08:30 -05002524{
Zev Weissa8f116a2021-09-01 21:08:30 -05002525 std::string event;
Zev Weissca478552024-06-11 23:45:58 +00002526 std::string thresholdInterface;
2527 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2528
2529 msg.read(thresholdInterface, propertiesChanged);
2530 if (propertiesChanged.empty())
2531 {
2532 return std::nullopt;
2533 }
2534
2535 event = propertiesChanged.begin()->first;
2536 if (event.empty() || event != name)
2537 {
2538 return std::nullopt;
2539 }
2540
2541 return std::get<T>(propertiesChanged.begin()->second);
2542}
2543
2544static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2545 const ConfigData& config, bool& value)
2546{
Zev Weissa8f116a2021-09-01 21:08:30 -05002547 try
2548 {
Zev Weissca478552024-06-11 23:45:58 +00002549 if (config.matchRegex.has_value())
Zev Weissa8f116a2021-09-01 21:08:30 -05002550 {
Zev Weissca478552024-06-11 23:45:58 +00002551 std::optional<std::string> s =
2552 getMessageValue<std::string>(msg, config.lineName);
2553 if (!s.has_value())
2554 {
2555 return false;
2556 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002557
Zev Weissca478552024-06-11 23:45:58 +00002558 std::smatch m;
2559 value = std::regex_match(s.value(), m, config.matchRegex.value());
2560 }
2561 else
Zev Weissa8f116a2021-09-01 21:08:30 -05002562 {
Zev Weissca478552024-06-11 23:45:58 +00002563 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2564 if (!v.has_value())
2565 {
2566 return false;
2567 }
2568 value = v.value();
Zev Weissa8f116a2021-09-01 21:08:30 -05002569 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002570 return true;
2571 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002572 catch (const std::exception& e)
Zev Weissa8f116a2021-09-01 21:08:30 -05002573 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002574 lg2::error(
2575 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
Zev Weissca478552024-06-11 23:45:58 +00002576 "DBUS_NAME", config.lineName, "ERROR", e);
Zev Weissa8f116a2021-09-01 21:08:30 -05002577 return false;
2578 }
2579}
2580
Patrick Williamsb84e7892025-02-01 08:22:06 -05002581static sdbusplus::bus::match_t dbusGPIOMatcher(
2582 const ConfigData& cfg, std::function<void(bool)> onMatch)
Zev Weissa8f116a2021-09-01 21:08:30 -05002583{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002584 auto pulseEventMatcherCallback =
2585 [&cfg, onMatch](sdbusplus::message_t& msg) {
2586 bool value = false;
2587 if (!getDbusMsgGPIOState(msg, cfg, value))
2588 {
2589 return;
2590 }
2591 onMatch(value);
2592 };
Zev Weissa8f116a2021-09-01 21:08:30 -05002593
Patrick Williams439b9c32022-07-22 19:26:53 -05002594 return sdbusplus::bus::match_t(
2595 static_cast<sdbusplus::bus_t&>(*conn),
Zev Weissa8f116a2021-09-01 21:08:30 -05002596 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2597 "PropertiesChanged',arg0='" +
Zev Weiss1cc79212024-06-19 22:14:57 +00002598 cfg.interface + "',path='" + cfg.path + "',sender='" +
2599 cfg.dbusName + "'",
Zev Weissa8f116a2021-09-01 21:08:30 -05002600 std::move(pulseEventMatcherCallback));
2601}
2602
Michal Orzeledd211e2022-10-28 13:10:16 +02002603// D-Bus property read functions
2604void reschedulePropertyRead(const ConfigData& configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302605
Michal Orzeledd211e2022-10-28 13:10:16 +02002606int getProperty(const ConfigData& configData)
2607{
2608 std::variant<bool> resp;
2609
2610 try
Priyatharshan P70120512020-09-16 18:47:20 +05302611 {
Michal Orzeledd211e2022-10-28 13:10:16 +02002612 auto method = conn->new_method_call(
2613 configData.dbusName.c_str(), configData.path.c_str(),
2614 "org.freedesktop.DBus.Properties", "Get");
2615 method.append(configData.interface.c_str(),
2616 configData.lineName.c_str());
2617
2618 auto reply = conn->call(method);
Michal Orzeledd211e2022-10-28 13:10:16 +02002619 reply.read(resp);
2620 }
2621 catch (const sdbusplus::exception_t& e)
2622 {
Patrick Williamsdc706df2025-11-04 22:55:42 -05002623 lg2::error(
2624 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}: {WHAT}",
2625 "PROPERTY", configData.lineName, "INTERFACE", configData.interface,
2626 "PATH", configData.path, "WHAT", e);
2627
Michal Orzeledd211e2022-10-28 13:10:16 +02002628 reschedulePropertyRead(configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302629 return -1;
2630 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002631
Logananth Sundararaj85e111e2021-11-11 13:13:13 +05302632 auto respValue = std::get_if<bool>(&resp);
Priyatharshan P70120512020-09-16 18:47:20 +05302633 if (!respValue)
2634 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002635 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2636 "PROPERTY", configData.lineName);
Priyatharshan P70120512020-09-16 18:47:20 +05302637 return -1;
2638 }
2639 return (*respValue);
2640}
Michal Orzeledd211e2022-10-28 13:10:16 +02002641
2642void setInitialValue(const ConfigData& configData, bool initialValue)
2643{
2644 if (configData.name == "PowerOk")
2645 {
2646 powerState = (initialValue ? PowerState::on : PowerState::off);
2647 hostIface->set_property("CurrentHostState",
2648 std::string(getHostState(powerState)));
2649 }
2650 else if (configData.name == "PowerButton")
2651 {
2652 powerButtonIface->set_property("ButtonPressed", !initialValue);
2653 }
2654 else if (configData.name == "ResetButton")
2655 {
2656 resetButtonIface->set_property("ButtonPressed", !initialValue);
2657 }
2658 else if (configData.name == "NMIButton")
2659 {
2660 nmiButtonIface->set_property("ButtonPressed", !initialValue);
2661 }
2662 else if (configData.name == "IdButton")
2663 {
2664 idButtonIface->set_property("ButtonPressed", !initialValue);
2665 }
2666 else if (configData.name == "PostComplete")
2667 {
2668 OperatingSystemStateStage osState =
Jason M. Billsc6d75652024-09-10 16:54:26 -07002669 (initialValue == postCompleteConfig.polarity
2670 ? OperatingSystemStateStage::Standby
2671 : OperatingSystemStateStage::Inactive);
Michal Orzeledd211e2022-10-28 13:10:16 +02002672 setOperatingSystemState(osState);
2673 }
2674 else
2675 {
2676 lg2::error("Unknown name {NAME}", "NAME", configData.name);
2677 }
2678}
2679
2680void reschedulePropertyRead(const ConfigData& configData)
2681{
2682 auto item = dBusRetryTimers.find(configData.name);
2683
2684 if (item == dBusRetryTimers.end())
2685 {
2686 auto newItem = dBusRetryTimers.insert(
2687 {configData.name, boost::asio::steady_timer(io)});
2688
2689 if (!newItem.second)
2690 {
2691 lg2::error("Failed to add new timer for {NAME}", "NAME",
2692 configData.name);
2693 return;
2694 }
2695
2696 item = newItem.first;
2697 }
2698
2699 auto& timer = item->second;
2700 timer.expires_after(
2701 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2702 timer.async_wait([&configData](const boost::system::error_code ec) {
2703 if (ec)
2704 {
2705 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2706 configData.name, "MSG", ec.message());
2707 dBusRetryTimers.erase(configData.name);
2708 return;
2709 }
Willy Tu4d684112025-08-17 22:15:25 +00002710 sdbusplus::asio::getProperty<bool>(
2711 *conn, configData.dbusName, configData.path, configData.interface,
2712 configData.lineName,
2713 [&configData](boost::system::error_code ec, bool value) {
2714 if (ec)
2715 {
2716 lg2::error("Exception while reading {PROPERTY}: {WHAT}",
2717 "PROPERTY", configData.lineName, "WHAT",
2718 ec.message());
2719 reschedulePropertyRead(configData);
2720 return;
2721 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002722
Willy Tu4d684112025-08-17 22:15:25 +00002723 setInitialValue(configData, value);
2724 dBusRetryTimers.erase(configData.name);
2725 });
Michal Orzeledd211e2022-10-28 13:10:16 +02002726 });
2727}
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002728} // namespace power_control
2729
2730int main(int argc, char* argv[])
2731{
Lei YU92caa4c2021-02-23 16:59:25 +08002732 using namespace power_control;
Priyatharshan P70120512020-09-16 18:47:20 +05302733
2734 if (argc > 1)
2735 {
2736 node = argv[1];
2737 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002738 lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2739 node);
Priyatharshan P70120512020-09-16 18:47:20 +05302740
Lei YU92caa4c2021-02-23 16:59:25 +08002741 conn = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002742
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302743 // Load GPIO's through json config file
Lei YU92caa4c2021-02-23 16:59:25 +08002744 if (loadConfigValues() == -1)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302745 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002746 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302747 }
Naveen Mosesec972d82021-07-16 21:19:23 +05302748 /* Currently for single host based systems additional busname is added
2749 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2750 Going forward for single hosts the old bus name without zero numbering
2751 will be removed when all other applications adapted to the
2752 bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2753
2754 if (node == "0")
2755 {
2756 // Request all the dbus names
2757 conn->request_name(hostDbusName.c_str());
2758 conn->request_name(chassisDbusName.c_str());
2759 conn->request_name(osDbusName.c_str());
2760 conn->request_name(buttonDbusName.c_str());
2761 conn->request_name(nmiDbusName.c_str());
2762 conn->request_name(rstCauseDbusName.c_str());
2763 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302764
Zev Weissc4005bd2021-09-01 22:30:23 -05002765 hostDbusName += node;
2766 chassisDbusName += node;
2767 osDbusName += node;
2768 buttonDbusName += node;
2769 nmiDbusName += node;
2770 rstCauseDbusName += node;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002771
Priyatharshan P70120512020-09-16 18:47:20 +05302772 // Request all the dbus names
2773 conn->request_name(hostDbusName.c_str());
2774 conn->request_name(chassisDbusName.c_str());
2775 conn->request_name(osDbusName.c_str());
2776 conn->request_name(buttonDbusName.c_str());
2777 conn->request_name(nmiDbusName.c_str());
2778 conn->request_name(rstCauseDbusName.c_str());
2779
2780 if (sioPwrGoodConfig.lineName.empty() ||
2781 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
Priyatharshan P19c47a32020-08-12 18:16:43 +05302782 {
Lei YU92caa4c2021-02-23 16:59:25 +08002783 sioEnabled = false;
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002784 lg2::info("SIO control GPIOs not defined, disable SIO support.");
Priyatharshan P19c47a32020-08-12 18:16:43 +05302785 }
2786
Jason M. Bills35471132025-05-06 12:26:10 -07002787 // Request power OK GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302788 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002789 {
Jason M. Bills35471132025-05-06 12:26:10 -07002790 if (!requestGPIOEvents(powerOkConfig.lineName, powerOKHandler,
2791 powerOKLine, powerOKEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302792 {
2793 return -1;
2794 }
2795 }
Priyatharshan P70120512020-09-16 18:47:20 +05302796 else if (powerOkConfig.type == ConfigType::DBUS)
2797 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002798 static sdbusplus::bus::match_t powerOkEventMonitor =
Jason M. Bills35471132025-05-06 12:26:10 -07002799 power_control::dbusGPIOMatcher(powerOkConfig, powerOKHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302800 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302801 else
2802 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002803 lg2::error("PowerOk name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002804 return -1;
2805 }
2806
Lei YU92caa4c2021-02-23 16:59:25 +08002807 if (sioEnabled == true)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002808 {
Priyatharshan P19c47a32020-08-12 18:16:43 +05302809 // Request SIO_POWER_GOOD GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302810 if (sioPwrGoodConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302811 {
Priyatharshan P70120512020-09-16 18:47:20 +05302812 if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002813 sioPowerGoodHandler, sioPowerGoodLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302814 sioPowerGoodEvent))
2815 {
2816 return -1;
2817 }
2818 }
2819 else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2820 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002821 static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002822 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2823 sioPowerGoodHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302824 }
2825 else
2826 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002827 lg2::error(
Priyatharshan P70120512020-09-16 18:47:20 +05302828 "sioPwrGood name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302829 return -1;
2830 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002831
Priyatharshan P19c47a32020-08-12 18:16:43 +05302832 // Request SIO_ONCONTROL GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302833 if (sioOnControlConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302834 {
Priyatharshan P70120512020-09-16 18:47:20 +05302835 if (!requestGPIOEvents(sioOnControlConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002836 sioOnControlHandler, sioOnControlLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302837 sioOnControlEvent))
2838 {
2839 return -1;
2840 }
2841 }
2842 else if (sioOnControlConfig.type == ConfigType::DBUS)
2843 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002844 static sdbusplus::bus::match_t sioOnControlEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002845 power_control::dbusGPIOMatcher(sioOnControlConfig,
2846 sioOnControlHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302847 }
2848 else
2849 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002850 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002851 "sioOnControl name should be configured from jsonconfig file\n");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302852 return -1;
2853 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002854
Priyatharshan P19c47a32020-08-12 18:16:43 +05302855 // Request SIO_S5 GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302856 if (sioS5Config.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302857 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002858 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
Priyatharshan P70120512020-09-16 18:47:20 +05302859 sioS5Line, sioS5Event))
2860 {
2861 return -1;
2862 }
2863 }
2864 else if (sioS5Config.type == ConfigType::DBUS)
2865 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002866 static sdbusplus::bus::match_t sioS5EventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002867 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
Priyatharshan P70120512020-09-16 18:47:20 +05302868 }
2869 else
2870 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002871 lg2::error("sioS5 name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302872 return -1;
2873 }
2874 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002875
2876 // Request POWER_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302877 if (powerButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002878 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002879 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2880 powerButtonLine, powerButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302881 {
2882 return -1;
2883 }
2884 }
Priyatharshan P70120512020-09-16 18:47:20 +05302885 else if (powerButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302886 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002887 static sdbusplus::bus::match_t powerButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002888 power_control::dbusGPIOMatcher(powerButtonConfig,
2889 powerButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002890 }
2891
2892 // Request RESET_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302893 if (resetButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002894 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002895 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2896 resetButtonLine, resetButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302897 {
2898 return -1;
2899 }
2900 }
Priyatharshan P70120512020-09-16 18:47:20 +05302901 else if (resetButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302902 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002903 static sdbusplus::bus::match_t resetButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002904 power_control::dbusGPIOMatcher(resetButtonConfig,
2905 resetButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002906 }
2907
2908 // Request NMI_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302909 if (nmiButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302910 {
Priyatharshan P70120512020-09-16 18:47:20 +05302911 if (!nmiButtonConfig.lineName.empty())
2912 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002913 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302914 nmiButtonLine, nmiButtonEvent);
2915 }
2916 }
2917 else if (nmiButtonConfig.type == ConfigType::DBUS)
2918 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002919 static sdbusplus::bus::match_t nmiButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002920 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302921 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002922
2923 // Request ID_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302924 if (idButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302925 {
Priyatharshan P70120512020-09-16 18:47:20 +05302926 if (!idButtonConfig.lineName.empty())
2927 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002928 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302929 idButtonLine, idButtonEvent);
2930 }
2931 }
2932 else if (idButtonConfig.type == ConfigType::DBUS)
2933 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002934 static sdbusplus::bus::match_t idButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002935 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302936 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002937
Jason M. Billsfb957332021-01-28 13:18:46 -08002938#ifdef USE_PLT_RST
Patrick Williams439b9c32022-07-22 19:26:53 -05002939 sdbusplus::bus::match_t pltRstMatch(
Lei YU92caa4c2021-02-23 16:59:25 +08002940 *conn,
Jason M. Billsfb957332021-01-28 13:18:46 -08002941 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2942 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
Lei YU92caa4c2021-02-23 16:59:25 +08002943 hostMiscHandler);
Jason M. Billsfb957332021-01-28 13:18:46 -08002944#endif
2945
Marc Olberding4ad47bf2025-10-24 09:13:10 -07002946 bool postCompleteConfigured = false;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002947 // Request POST_COMPLETE GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302948 if (postCompleteConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002949 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002950 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2951 postCompleteLine, postCompleteEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302952 {
2953 return -1;
2954 }
Marc Olberding4ad47bf2025-10-24 09:13:10 -07002955 postCompleteConfigured = true;
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302956 }
Priyatharshan P70120512020-09-16 18:47:20 +05302957 else if (postCompleteConfig.type == ConfigType::DBUS)
2958 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002959 static sdbusplus::bus::match_t postCompleteEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002960 power_control::dbusGPIOMatcher(postCompleteConfig,
2961 postCompleteHandler);
Marc Olberding4ad47bf2025-10-24 09:13:10 -07002962 postCompleteConfigured = true;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002963 }
2964
2965 // initialize NMI_OUT GPIO.
Priyatharshan P70120512020-09-16 18:47:20 +05302966 if (!nmiOutConfig.lineName.empty())
2967 {
Jian Zhang461a1662022-09-22 11:29:01 +08002968 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2969 nmiOutLine);
Priyatharshan P70120512020-09-16 18:47:20 +05302970 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002971
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002972 // Initialize POWER_OUT and RESET_OUT GPIO.
2973 gpiod::line line;
Priyatharshan P70120512020-09-16 18:47:20 +05302974 if (!powerOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002975 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002976 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2977 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302978 {
2979 return -1;
2980 }
2981 }
2982 else
2983 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002984 lg2::error("powerOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002985 return -1;
2986 }
2987
Priyatharshan P70120512020-09-16 18:47:20 +05302988 if (!resetOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002989 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002990 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2991 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302992 {
2993 return -1;
2994 }
2995 }
2996 else
2997 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002998 lg2::error("ResetOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002999 return -1;
3000 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07003001 // Release line
3002 line.reset();
3003
Matt Simmering58e379d2022-09-23 14:45:50 -07003004 // Initialize the power state and operating system state
Lei YU92caa4c2021-02-23 16:59:25 +08003005 powerState = PowerState::off;
Matt Simmering58e379d2022-09-23 14:45:50 -07003006 operatingSystemState = OperatingSystemStateStage::Inactive;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003007 // Check power good
Priyatharshan P70120512020-09-16 18:47:20 +05303008
3009 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003010 {
Jason M. Bills35471132025-05-06 12:26:10 -07003011 if (powerOKLine.get_value() > 0 ||
Lei YUa37c2472021-09-26 15:57:12 +08003012 (sioEnabled &&
3013 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
Priyatharshan P70120512020-09-16 18:47:20 +05303014 {
3015 powerState = PowerState::on;
3016 }
3017 }
3018 else
3019 {
3020 if (getProperty(powerOkConfig))
3021 {
3022 powerState = PowerState::on;
3023 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003024 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003025 // Check if we need to start the Power Restore policy
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03003026 if (powerState != PowerState::on)
3027 {
3028 powerRestore.run();
3029 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003030
Lei YU92caa4c2021-02-23 16:59:25 +08003031 if (nmiOutLine)
3032 nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003033
Jason M. Billsc46ebb42021-11-10 11:41:31 -08003034 lg2::info("Initializing power state.");
Lei YU92caa4c2021-02-23 16:59:25 +08003035 logStateTransition(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003036
3037 // Power Control Service
3038 sdbusplus::asio::object_server hostServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003039 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003040
3041 // Power Control Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303042 hostIface =
3043 hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
3044 "xyz.openbmc_project.State.Host");
Vernon Maueryb4d03b12021-05-26 19:11:41 -07003045 // Interface for IPMI/Redfish initiated host state transitions
Lei YU92caa4c2021-02-23 16:59:25 +08003046 hostIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003047 "RequestedHostTransition",
3048 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
3049 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003050 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003051 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003052 // if power button is masked, ignore this
3053 if (!powerButtonMask)
3054 {
3055 sendPowerControlEvent(Event::gracefulPowerOffRequest);
3056 addRestartCause(RestartCause::command);
3057 }
3058 else
3059 {
3060 lg2::info("Power Button Masked.");
3061 throw std::invalid_argument("Transition Request Masked");
3062 return 0;
3063 }
3064 }
3065 else if (requested ==
3066 "xyz.openbmc_project.State.Host.Transition.On")
3067 {
3068 // if power button is masked, ignore this
3069 if (!powerButtonMask)
3070 {
3071 sendPowerControlEvent(Event::powerOnRequest);
3072 addRestartCause(RestartCause::command);
3073 }
3074 else
3075 {
3076 lg2::info("Power Button Masked.");
3077 throw std::invalid_argument("Transition Request Masked");
3078 return 0;
3079 }
3080 }
3081 else if (requested ==
3082 "xyz.openbmc_project.State.Host.Transition.Reboot")
3083 {
3084 // if power button is masked, ignore this
3085 if (!powerButtonMask)
3086 {
3087 sendPowerControlEvent(Event::powerCycleRequest);
3088 addRestartCause(RestartCause::command);
3089 }
3090 else
3091 {
3092 lg2::info("Power Button Masked.");
3093 throw std::invalid_argument("Transition Request Masked");
3094 return 0;
3095 }
3096 }
3097 else if (
3098 requested ==
3099 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3100 {
3101 // if reset button is masked, ignore this
3102 if (!resetButtonMask)
3103 {
3104 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3105 addRestartCause(RestartCause::command);
3106 }
3107 else
3108 {
3109 lg2::info("Reset Button Masked.");
3110 throw std::invalid_argument("Transition Request Masked");
3111 return 0;
3112 }
3113 }
3114 else if (
3115 requested ==
3116 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3117 {
3118 // if reset button is masked, ignore this
3119 if (!resetButtonMask)
3120 {
3121 sendPowerControlEvent(Event::resetRequest);
3122 addRestartCause(RestartCause::command);
3123 }
3124 else
3125 {
3126 lg2::info("Reset Button Masked.");
3127 throw std::invalid_argument("Transition Request Masked");
3128 return 0;
3129 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08003130 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003131 else
3132 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003133 lg2::error("Unrecognized host state transition request.");
3134 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003135 return 0;
3136 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003137 resp = requested;
3138 return 1;
3139 });
Lei YU92caa4c2021-02-23 16:59:25 +08003140 hostIface->register_property("CurrentHostState",
3141 std::string(getHostState(powerState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003142
Lei YU92caa4c2021-02-23 16:59:25 +08003143 hostIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003144
3145 // Chassis Control Service
3146 sdbusplus::asio::object_server chassisServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003147 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003148
3149 // Chassis Control Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003150 chassisIface =
Priyatharshan P70120512020-09-16 18:47:20 +05303151 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003152 "xyz.openbmc_project.State.Chassis");
3153
Lei YU92caa4c2021-02-23 16:59:25 +08003154 chassisIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003155 "RequestedPowerTransition",
3156 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3157 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003158 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003159 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003160 // if power button is masked, ignore this
3161 if (!powerButtonMask)
3162 {
3163 sendPowerControlEvent(Event::powerOffRequest);
3164 addRestartCause(RestartCause::command);
3165 }
3166 else
3167 {
3168 lg2::info("Power Button Masked.");
3169 throw std::invalid_argument("Transition Request Masked");
3170 return 0;
3171 }
3172 }
3173 else if (requested ==
3174 "xyz.openbmc_project.State.Chassis.Transition.On")
3175 {
3176 // if power button is masked, ignore this
3177 if (!powerButtonMask)
3178 {
3179 sendPowerControlEvent(Event::powerOnRequest);
3180 addRestartCause(RestartCause::command);
3181 }
3182 else
3183 {
3184 lg2::info("Power Button Masked.");
3185 throw std::invalid_argument("Transition Request Masked");
3186 return 0;
3187 }
3188 }
3189 else if (requested ==
3190 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3191 {
3192 // if power button is masked, ignore this
3193 if (!powerButtonMask)
3194 {
3195 sendPowerControlEvent(Event::powerCycleRequest);
3196 addRestartCause(RestartCause::command);
3197 }
3198 else
3199 {
3200 lg2::info("Power Button Masked.");
3201 throw std::invalid_argument("Transition Request Masked");
3202 return 0;
3203 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003204 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003205 else
3206 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003207 lg2::error("Unrecognized chassis state transition request.");
3208 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003209 return 0;
3210 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003211 resp = requested;
3212 return 1;
3213 });
Lei YU92caa4c2021-02-23 16:59:25 +08003214 chassisIface->register_property("CurrentPowerState",
3215 std::string(getChassisState(powerState)));
3216 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003217
Lei YU92caa4c2021-02-23 16:59:25 +08003218 chassisIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003219
Vijay Khemka04175c22020-10-09 14:28:11 -07003220#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003221 // Chassis System Service
3222 sdbusplus::asio::object_server chassisSysServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003223 sdbusplus::asio::object_server(conn);
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003224
3225 // Chassis System Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003226 chassisSysIface = chassisSysServer.add_interface(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003227 "/xyz/openbmc_project/state/chassis_system0",
3228 "xyz.openbmc_project.State.Chassis");
3229
Lei YU92caa4c2021-02-23 16:59:25 +08003230 chassisSysIface->register_property(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003231 "RequestedPowerTransition",
3232 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3233 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003234 if (requested ==
3235 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3236 {
3237 systemReset();
3238 addRestartCause(RestartCause::command);
3239 }
3240 else
3241 {
3242 lg2::error(
3243 "Unrecognized chassis system state transition request.");
3244 throw std::invalid_argument("Unrecognized Transition Request");
3245 return 0;
3246 }
3247 resp = requested;
3248 return 1;
3249 });
Lei YU92caa4c2021-02-23 16:59:25 +08003250 chassisSysIface->register_property(
3251 "CurrentPowerState", std::string(getChassisState(powerState)));
3252 chassisSysIface->register_property("LastStateChangeTime",
3253 getCurrentTimeMs());
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003254
Lei YU92caa4c2021-02-23 16:59:25 +08003255 chassisSysIface->initialize();
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003256
Naveen Moses117c34e2021-05-26 20:10:51 +05303257 if (!slotPowerConfig.lineName.empty())
3258 {
3259 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3260 {
3261 return -1;
3262 }
3263
3264 slotPowerState = SlotPowerState::off;
3265 if (slotPowerLine.get_value() > 0)
3266 {
3267 slotPowerState = SlotPowerState::on;
3268 }
3269
3270 chassisSlotIface = chassisSysServer.add_interface(
3271 "/xyz/openbmc_project/state/chassis_system" + node,
3272 "xyz.openbmc_project.State.Chassis");
3273 chassisSlotIface->register_property(
3274 "RequestedPowerTransition",
3275 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3276 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003277 if (requested ==
3278 "xyz.openbmc_project.State.Chassis.Transition.On")
3279 {
3280 slotPowerOn();
3281 }
3282 else if (requested ==
3283 "xyz.openbmc_project.State.Chassis.Transition.Off")
3284 {
3285 slotPowerOff();
3286 }
3287 else if (
3288 requested ==
3289 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3290 {
3291 slotPowerCycle();
3292 }
3293 else
3294 {
3295 lg2::error(
3296 "Unrecognized chassis system state transition request.\n");
3297 throw std::invalid_argument(
3298 "Unrecognized Transition Request");
3299 return 0;
3300 }
3301 resp = requested;
3302 return 1;
3303 });
Naveen Moses117c34e2021-05-26 20:10:51 +05303304 chassisSlotIface->register_property(
3305 "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3306 chassisSlotIface->register_property("LastStateChangeTime",
3307 getCurrentTimeMs());
3308 chassisSlotIface->initialize();
3309 }
3310#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003311 // Buttons Service
3312 sdbusplus::asio::object_server buttonsServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003313 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003314
Priyatharshan P70120512020-09-16 18:47:20 +05303315 if (!powerButtonConfig.lineName.empty())
John Wang6c090072020-09-30 13:32:16 +08003316 {
Priyatharshan P70120512020-09-16 18:47:20 +05303317 // Power Button Interface
3318 power_control::powerButtonIface = buttonsServer.add_interface(
3319 "/xyz/openbmc_project/chassis/buttons/power",
3320 "xyz.openbmc_project.Chassis.Buttons");
3321
3322 powerButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003323 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003324 if (requested)
Priyatharshan P70120512020-09-16 18:47:20 +05303325 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003326 if (powerButtonMask)
3327 {
3328 return 1;
3329 }
3330 if (!setGPIOOutput(powerOutConfig.lineName,
3331 !powerOutConfig.polarity,
3332 powerButtonMask))
3333 {
3334 throw std::runtime_error("Failed to request GPIO");
3335 return 0;
3336 }
3337 lg2::info("Power Button Masked.");
Priyatharshan P70120512020-09-16 18:47:20 +05303338 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003339 else
Priyatharshan P70120512020-09-16 18:47:20 +05303340 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003341 if (!powerButtonMask)
3342 {
3343 return 1;
3344 }
3345 lg2::info("Power Button Un-masked");
3346 powerButtonMask.reset();
Priyatharshan P70120512020-09-16 18:47:20 +05303347 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003348 // Update the mask setting
3349 current = requested;
3350 return 1;
3351 });
Priyatharshan P70120512020-09-16 18:47:20 +05303352
3353 // Check power button state
3354 bool powerButtonPressed;
3355 if (powerButtonConfig.type == ConfigType::GPIO)
3356 {
3357 powerButtonPressed = powerButtonLine.get_value() == 0;
3358 }
3359 else
3360 {
3361 powerButtonPressed = getProperty(powerButtonConfig) == 0;
3362 }
3363
3364 powerButtonIface->register_property("ButtonPressed",
3365 powerButtonPressed);
3366
3367 powerButtonIface->initialize();
3368 }
3369
3370 if (!resetButtonConfig.lineName.empty())
3371 {
3372 // Reset Button Interface
3373
Lei YU92caa4c2021-02-23 16:59:25 +08003374 resetButtonIface = buttonsServer.add_interface(
John Wang6c090072020-09-30 13:32:16 +08003375 "/xyz/openbmc_project/chassis/buttons/reset",
3376 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003377
Lei YU92caa4c2021-02-23 16:59:25 +08003378 resetButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003379 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003380 if (requested)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003381 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003382 if (resetButtonMask)
3383 {
3384 return 1;
3385 }
3386 if (!setGPIOOutput(resetOutConfig.lineName,
3387 !resetOutConfig.polarity,
3388 resetButtonMask))
3389 {
3390 throw std::runtime_error("Failed to request GPIO");
3391 return 0;
3392 }
3393 lg2::info("Reset Button Masked.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003394 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003395 else
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003396 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003397 if (!resetButtonMask)
3398 {
3399 return 1;
3400 }
3401 lg2::info("Reset Button Un-masked");
3402 resetButtonMask.reset();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003403 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003404 // Update the mask setting
3405 current = requested;
3406 return 1;
3407 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003408
John Wang6c090072020-09-30 13:32:16 +08003409 // Check reset button state
Priyatharshan P70120512020-09-16 18:47:20 +05303410 bool resetButtonPressed;
3411 if (resetButtonConfig.type == ConfigType::GPIO)
3412 {
3413 resetButtonPressed = resetButtonLine.get_value() == 0;
3414 }
3415 else
3416 {
3417 resetButtonPressed = getProperty(resetButtonConfig) == 0;
3418 }
3419
Lei YU92caa4c2021-02-23 16:59:25 +08003420 resetButtonIface->register_property("ButtonPressed",
3421 resetButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003422
Lei YU92caa4c2021-02-23 16:59:25 +08003423 resetButtonIface->initialize();
John Wang6c090072020-09-30 13:32:16 +08003424 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003425
Lei YU92caa4c2021-02-23 16:59:25 +08003426 if (nmiButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003427 {
3428 // NMI Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003429 nmiButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003430 "/xyz/openbmc_project/chassis/buttons/nmi",
3431 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003432
Lei YU92caa4c2021-02-23 16:59:25 +08003433 nmiButtonIface->register_property(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003434 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003435 if (nmiButtonMasked == requested)
3436 {
3437 // NMI button mask is already set as requested, so no change
3438 return 1;
3439 }
3440 if (requested)
3441 {
3442 lg2::info("NMI Button Masked.");
3443 nmiButtonMasked = true;
3444 }
3445 else
3446 {
3447 lg2::info("NMI Button Un-masked.");
3448 nmiButtonMasked = false;
3449 }
3450 // Update the mask setting
3451 current = nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003452 return 1;
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003453 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003454
Vijay Khemka33a532d2019-11-14 16:50:35 -08003455 // Check NMI button state
Priyatharshan P70120512020-09-16 18:47:20 +05303456 bool nmiButtonPressed;
3457 if (nmiButtonConfig.type == ConfigType::GPIO)
3458 {
3459 nmiButtonPressed = nmiButtonLine.get_value() == 0;
3460 }
3461 else
3462 {
3463 nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3464 }
3465
Lei YU92caa4c2021-02-23 16:59:25 +08003466 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003467
Lei YU92caa4c2021-02-23 16:59:25 +08003468 nmiButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003469 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003470
Lei YU92caa4c2021-02-23 16:59:25 +08003471 if (nmiOutLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003472 {
3473 // NMI out Service
3474 sdbusplus::asio::object_server nmiOutServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003475 sdbusplus::asio::object_server(conn);
Chen Yugang174ec662019-08-19 19:58:49 +08003476
Vijay Khemka33a532d2019-11-14 16:50:35 -08003477 // NMI out Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303478 nmiOutIface = nmiOutServer.add_interface(
3479 "/xyz/openbmc_project/control/host" + node + "/nmi",
3480 "xyz.openbmc_project.Control.Host.NMI");
Lei YU92caa4c2021-02-23 16:59:25 +08003481 nmiOutIface->register_method("NMI", nmiReset);
3482 nmiOutIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003483 }
Chen Yugang174ec662019-08-19 19:58:49 +08003484
Lei YU92caa4c2021-02-23 16:59:25 +08003485 if (idButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003486 {
3487 // ID Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003488 idButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003489 "/xyz/openbmc_project/chassis/buttons/id",
3490 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003491
Vijay Khemka33a532d2019-11-14 16:50:35 -08003492 // Check ID button state
Priyatharshan P70120512020-09-16 18:47:20 +05303493 bool idButtonPressed;
3494 if (idButtonConfig.type == ConfigType::GPIO)
3495 {
3496 idButtonPressed = idButtonLine.get_value() == 0;
3497 }
3498 else
3499 {
3500 idButtonPressed = getProperty(idButtonConfig) == 0;
3501 }
3502
Lei YU92caa4c2021-02-23 16:59:25 +08003503 idButtonIface->register_property("ButtonPressed", idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003504
Lei YU92caa4c2021-02-23 16:59:25 +08003505 idButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003506 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003507
Marc Olberding4ad47bf2025-10-24 09:13:10 -07003508 if (postCompleteConfigured)
Priyatharshan P70120512020-09-16 18:47:20 +05303509 {
Marc Olberding4ad47bf2025-10-24 09:13:10 -07003510 OperatingSystemStateStage osState;
3511 if (postCompleteConfig.type == ConfigType::GPIO)
3512 {
3513 osState =
3514 postCompleteLine.get_value() == postCompleteConfig.polarity
3515 ? OperatingSystemStateStage::Standby
3516 : OperatingSystemStateStage::Inactive;
3517 }
3518 else
3519 {
3520 osState = getProperty(postCompleteConfig) > 0
3521 ? OperatingSystemStateStage::Standby
3522 : OperatingSystemStateStage::Inactive;
3523 }
3524
3525 // OS State Service
3526 sdbusplus::asio::object_server osServer =
3527 sdbusplus::asio::object_server(conn);
3528
3529 // OS State Interface
3530 osIface = osServer.add_interface(
3531 "/xyz/openbmc_project/state/host" + node,
3532 "xyz.openbmc_project.State.OperatingSystem.Status");
3533
3534 // Get the initial OS state based on POST complete
3535 // Asserted, OS state is "Standby" (ready to boot)
3536 // De-Asserted, OS state is "Inactive"
3537
3538 osIface->register_property(
3539 "OperatingSystemState",
3540 std::string(getOperatingSystemStateStage(osState)));
3541
3542 osIface->initialize();
3543
3544 // Restart Cause Service
3545 sdbusplus::asio::object_server restartCauseServer =
3546 sdbusplus::asio::object_server(conn);
3547
3548 // Restart Cause Interface
3549 restartCauseIface = restartCauseServer.add_interface(
3550 "/xyz/openbmc_project/control/host" + node + "/restart_cause",
3551 "xyz.openbmc_project.Control.Host.RestartCause");
3552
3553 restartCauseIface->register_property(
3554 "RestartCause",
3555 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3556
3557 restartCauseIface->register_property(
3558 "RequestedRestartCause",
3559 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3560 [](const std::string& requested, std::string& resp) {
3561 if (requested ==
3562 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3563 {
3564 addRestartCause(RestartCause::watchdog);
3565 }
3566 else
3567 {
3568 throw std::invalid_argument(
3569 "Unrecognized RestartCause Request");
3570 return 0;
3571 }
3572
3573 lg2::info("RestartCause requested: {RESTART_CAUSE}",
3574 "RESTART_CAUSE", requested);
3575 resp = requested;
3576 return 1;
3577 });
3578
3579 restartCauseIface->initialize();
3580
3581 currentHostStateMonitor();
Priyatharshan P70120512020-09-16 18:47:20 +05303582 }
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +03003583 if (!hpmStbyEnConfig.lineName.empty())
3584 {
3585 // Set to indicate BMC's power control module is ready to take
3586 // the inputs [PWR_GOOD] from the HPM FPGA
3587 gpiod::line hpmLine;
3588 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3589 hpmLine))
3590 {
3591 return -1;
3592 }
3593 }
3594
Lei YU92caa4c2021-02-23 16:59:25 +08003595 io.run();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003596
3597 return 0;
3598}