blob: faefe9b8323dbcfc918f7c1403beb6b5ddf9df5e [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{
Matt Simmering58e379d2022-09-23 14:45:50 -0700252 operatingSystemState = stage;
253#if IGNORE_SOFT_RESETS_DURING_POST
254 // If POST complete has asserted set ignoreNextSoftReset to false to avoid
255 // masking soft resets after POST
256 if (operatingSystemState == OperatingSystemStateStage::Standby)
257 {
258 ignoreNextSoftReset = false;
259 }
260#endif
Tim Lee86239182021-12-23 11:46:01 +0800261 osIface->set_property("OperatingSystemState",
262 std::string(getOperatingSystemStateStage(stage)));
263
264 lg2::info("Moving os state to {STATE} stage", "STATE",
265 getOperatingSystemStateStage(stage));
266}
267
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700268enum class PowerState
269{
270 on,
Jason M. Bills35471132025-05-06 12:26:10 -0700271 waitForPowerOK,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700272 waitForSIOPowerGood,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700273 off,
274 transitionToOff,
275 gracefulTransitionToOff,
276 cycleOff,
277 transitionToCycleOff,
278 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700279 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700280};
281static PowerState powerState;
282static std::string getPowerStateName(PowerState state)
283{
284 switch (state)
285 {
286 case PowerState::on:
287 return "On";
288 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700289 case PowerState::waitForPowerOK:
290 return "Wait for Power OK";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700291 break;
292 case PowerState::waitForSIOPowerGood:
293 return "Wait for SIO Power Good";
294 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700295 case PowerState::off:
296 return "Off";
297 break;
298 case PowerState::transitionToOff:
299 return "Transition to Off";
300 break;
301 case PowerState::gracefulTransitionToOff:
302 return "Graceful Transition to Off";
303 break;
304 case PowerState::cycleOff:
305 return "Power Cycle Off";
306 break;
307 case PowerState::transitionToCycleOff:
308 return "Transition to Power Cycle Off";
309 break;
310 case PowerState::gracefulTransitionToCycleOff:
311 return "Graceful Transition to Power Cycle Off";
312 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700313 case PowerState::checkForWarmReset:
314 return "Check for Warm Reset";
315 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700316 default:
317 return "unknown state: " + std::to_string(static_cast<int>(state));
318 break;
319 }
320}
321static void logStateTransition(const PowerState state)
322{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800323 lg2::info("Host{HOST}: Moving to \"{STATE}\" state", "HOST", node, "STATE",
324 getPowerStateName(state));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700325}
326
327enum class Event
328{
Jason M. Bills35471132025-05-06 12:26:10 -0700329 powerOKAssert,
330 powerOKDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700331 sioPowerGoodAssert,
332 sioPowerGoodDeAssert,
333 sioS5Assert,
334 sioS5DeAssert,
Jason M. Billsfb957332021-01-28 13:18:46 -0800335 pltRstAssert,
336 pltRstDeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700337 postCompleteAssert,
338 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700339 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700340 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700341 powerCycleTimerExpired,
Jason M. Bills35471132025-05-06 12:26:10 -0700342 powerOKWatchdogTimerExpired,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700343 sioPowerGoodWatchdogTimerExpired,
344 gracefulPowerOffTimerExpired,
345 powerOnRequest,
346 powerOffRequest,
347 powerCycleRequest,
348 resetRequest,
349 gracefulPowerOffRequest,
350 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700351 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700352};
353static std::string getEventName(Event event)
354{
355 switch (event)
356 {
Jason M. Bills35471132025-05-06 12:26:10 -0700357 case Event::powerOKAssert:
358 return "power OK assert";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700359 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700360 case Event::powerOKDeAssert:
361 return "power OK de-assert";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700362 break;
363 case Event::sioPowerGoodAssert:
364 return "SIO power good assert";
365 break;
366 case Event::sioPowerGoodDeAssert:
367 return "SIO power good de-assert";
368 break;
369 case Event::sioS5Assert:
370 return "SIO S5 assert";
371 break;
372 case Event::sioS5DeAssert:
373 return "SIO S5 de-assert";
374 break;
Jason M. Billsfb957332021-01-28 13:18:46 -0800375 case Event::pltRstAssert:
376 return "PLT_RST assert";
377 break;
378 case Event::pltRstDeAssert:
379 return "PLT_RST de-assert";
380 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700381 case Event::postCompleteAssert:
382 return "POST Complete assert";
383 break;
384 case Event::postCompleteDeAssert:
385 return "POST Complete de-assert";
386 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700387 case Event::powerButtonPressed:
388 return "power button pressed";
389 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700390 case Event::resetButtonPressed:
391 return "reset button pressed";
392 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700393 case Event::powerCycleTimerExpired:
394 return "power cycle timer expired";
395 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700396 case Event::powerOKWatchdogTimerExpired:
397 return "power OK watchdog timer expired";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700398 break;
399 case Event::sioPowerGoodWatchdogTimerExpired:
400 return "SIO power good watchdog timer expired";
401 break;
402 case Event::gracefulPowerOffTimerExpired:
403 return "graceful power-off timer expired";
404 break;
405 case Event::powerOnRequest:
406 return "power-on request";
407 break;
408 case Event::powerOffRequest:
409 return "power-off request";
410 break;
411 case Event::powerCycleRequest:
412 return "power-cycle request";
413 break;
414 case Event::resetRequest:
415 return "reset request";
416 break;
417 case Event::gracefulPowerOffRequest:
418 return "graceful power-off request";
419 break;
420 case Event::gracefulPowerCycleRequest:
421 return "graceful power-cycle request";
422 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700423 case Event::warmResetDetected:
424 return "warm reset detected";
425 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700426 default:
427 return "unknown event: " + std::to_string(static_cast<int>(event));
428 break;
429 }
430}
431static void logEvent(const std::string_view stateHandler, const Event event)
432{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800433 lg2::info("{STATE_HANDLER}: {EVENT} event received", "STATE_HANDLER",
434 stateHandler, "EVENT", getEventName(event));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700435}
436
437// Power state handlers
438static void powerStateOn(const Event event);
Jason M. Bills35471132025-05-06 12:26:10 -0700439static void powerStateWaitForPowerOK(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700440static void powerStateWaitForSIOPowerGood(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700441static void powerStateOff(const Event event);
442static void powerStateTransitionToOff(const Event event);
443static void powerStateGracefulTransitionToOff(const Event event);
444static void powerStateCycleOff(const Event event);
445static void powerStateTransitionToCycleOff(const Event event);
446static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700447static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700448
449static std::function<void(const Event)> getPowerStateHandler(PowerState state)
450{
451 switch (state)
452 {
453 case PowerState::on:
454 return powerStateOn;
455 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700456 case PowerState::waitForPowerOK:
457 return powerStateWaitForPowerOK;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700458 break;
459 case PowerState::waitForSIOPowerGood:
460 return powerStateWaitForSIOPowerGood;
461 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700462 case PowerState::off:
463 return powerStateOff;
464 break;
465 case PowerState::transitionToOff:
466 return powerStateTransitionToOff;
467 break;
468 case PowerState::gracefulTransitionToOff:
469 return powerStateGracefulTransitionToOff;
470 break;
471 case PowerState::cycleOff:
472 return powerStateCycleOff;
473 break;
474 case PowerState::transitionToCycleOff:
475 return powerStateTransitionToCycleOff;
476 break;
477 case PowerState::gracefulTransitionToCycleOff:
478 return powerStateGracefulTransitionToCycleOff;
479 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700480 case PowerState::checkForWarmReset:
481 return powerStateCheckForWarmReset;
482 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700483 default:
Zev Weiss047bcb52020-08-20 21:28:11 +0000484 return nullptr;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700485 break;
486 }
487};
488
489static void sendPowerControlEvent(const Event event)
490{
491 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
492 if (handler == nullptr)
493 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800494 lg2::error("Failed to find handler for power state: {STATE}", "STATE",
495 static_cast<int>(powerState));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700496 return;
497 }
498 handler(event);
499}
500
501static uint64_t getCurrentTimeMs()
502{
503 struct timespec time = {};
504
505 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
506 {
507 return 0;
508 }
509 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
510 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
511
512 return currentTimeMs;
513}
514
515static constexpr std::string_view getHostState(const PowerState state)
516{
517 switch (state)
518 {
519 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700520 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700521 case PowerState::gracefulTransitionToCycleOff:
522 return "xyz.openbmc_project.State.Host.HostState.Running";
523 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700524 case PowerState::waitForPowerOK:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700525 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700526 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700527 case PowerState::transitionToOff:
528 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700529 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700530 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700531 return "xyz.openbmc_project.State.Host.HostState.Off";
532 break;
533 default:
534 return "";
535 break;
536 }
537};
538static constexpr std::string_view getChassisState(const PowerState state)
539{
540 switch (state)
541 {
542 case PowerState::on:
543 case PowerState::transitionToOff:
544 case PowerState::gracefulTransitionToOff:
545 case PowerState::transitionToCycleOff:
546 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700547 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700548 return "xyz.openbmc_project.State.Chassis.PowerState.On";
549 break;
Jason M. Bills35471132025-05-06 12:26:10 -0700550 case PowerState::waitForPowerOK:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700551 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700552 case PowerState::off:
553 case PowerState::cycleOff:
554 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
555 break;
556 default:
557 return "";
558 break;
559 }
560};
Naveen Moses117c34e2021-05-26 20:10:51 +0530561#ifdef CHASSIS_SYSTEM_RESET
562enum class SlotPowerState
563{
564 on,
565 off,
566};
567static SlotPowerState slotPowerState;
568static constexpr std::string_view getSlotState(const SlotPowerState state)
569{
570 switch (state)
571 {
572 case SlotPowerState::on:
573 return "xyz.openbmc_project.State.Chassis.PowerState.On";
574 break;
575 case SlotPowerState::off:
576 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
577 break;
578 default:
579 return "";
580 break;
581 }
582};
583static void setSlotPowerState(const SlotPowerState state)
584{
585 slotPowerState = state;
586 chassisSlotIface->set_property("CurrentPowerState",
587 std::string(getSlotState(slotPowerState)));
588 chassisSlotIface->set_property("LastStateChangeTime", getCurrentTimeMs());
589}
590#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700591static void savePowerState(const PowerState state)
592{
593 powerStateSaveTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -0700594 std::chrono::milliseconds(TimerMap["PowerOffSaveMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700595 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
596 if (ec)
597 {
598 // operation_aborted is expected if timer is canceled before
599 // completion.
600 if (ec != boost::asio::error::operation_aborted)
601 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800602 lg2::error("Power-state save async_wait failed: {ERROR_MSG}",
603 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700604 }
605 return;
606 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300607 appState.set(PersistentState::Params::PowerState,
608 std::string{getChassisState(state)});
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700609 });
610}
611static void setPowerState(const PowerState state)
612{
613 powerState = state;
614 logStateTransition(state);
615
616 hostIface->set_property("CurrentHostState",
617 std::string(getHostState(powerState)));
618
619 chassisIface->set_property("CurrentPowerState",
620 std::string(getChassisState(powerState)));
621 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
622
623 // Save the power state for the restore policy
624 savePowerState(state);
625}
626
627enum class RestartCause
628{
629 command,
630 resetButton,
631 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700632 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700633 powerPolicyOn,
634 powerPolicyRestore,
635 softReset,
636};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700637static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700638static std::string getRestartCause(RestartCause cause)
639{
640 switch (cause)
641 {
642 case RestartCause::command:
643 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
644 break;
645 case RestartCause::resetButton:
646 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
647 break;
648 case RestartCause::powerButton:
649 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
650 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700651 case RestartCause::watchdog:
652 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
653 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700654 case RestartCause::powerPolicyOn:
Jason M. Bills418ce112021-09-08 15:15:05 -0700655 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700656 break;
657 case RestartCause::powerPolicyRestore:
Jason M. Bills418ce112021-09-08 15:15:05 -0700658 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyPreviousState";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700659 break;
660 case RestartCause::softReset:
661 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
662 break;
663 default:
664 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
665 break;
666 }
667}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700668static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700669{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700670 // Add this to the set of causes for this restart
671 causeSet.insert(cause);
672}
673static void clearRestartCause()
674{
675 // Clear the set for the next restart
676 causeSet.clear();
677}
678static void setRestartCauseProperty(const std::string& cause)
679{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800680 lg2::info("RestartCause set to {RESTART_CAUSE}", "RESTART_CAUSE", cause);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700681 restartCauseIface->set_property("RestartCause", cause);
682}
Rashmi RV89f61312020-01-22 15:41:50 +0530683
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300684#ifdef USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530685static void resetACBootProperty()
686{
687 if ((causeSet.contains(RestartCause::command)) ||
688 (causeSet.contains(RestartCause::softReset)))
689 {
690 conn->async_method_call(
691 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400692 if (ec)
693 {
694 lg2::error("failed to reset ACBoot property");
695 }
696 },
Rashmi RV89f61312020-01-22 15:41:50 +0530697 "xyz.openbmc_project.Settings",
698 "/xyz/openbmc_project/control/host0/ac_boot",
699 "org.freedesktop.DBus.Properties", "Set",
700 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
701 std::variant<std::string>{"False"});
702 }
703}
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300704#endif // USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530705
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700706static void setRestartCause()
707{
708 // Determine the actual restart cause based on the set of causes
709 std::string restartCause =
710 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
711 if (causeSet.contains(RestartCause::watchdog))
712 {
713 restartCause = getRestartCause(RestartCause::watchdog);
714 }
715 else if (causeSet.contains(RestartCause::command))
716 {
717 restartCause = getRestartCause(RestartCause::command);
718 }
719 else if (causeSet.contains(RestartCause::resetButton))
720 {
721 restartCause = getRestartCause(RestartCause::resetButton);
722 }
723 else if (causeSet.contains(RestartCause::powerButton))
724 {
725 restartCause = getRestartCause(RestartCause::powerButton);
726 }
727 else if (causeSet.contains(RestartCause::powerPolicyOn))
728 {
729 restartCause = getRestartCause(RestartCause::powerPolicyOn);
730 }
731 else if (causeSet.contains(RestartCause::powerPolicyRestore))
732 {
733 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
734 }
735 else if (causeSet.contains(RestartCause::softReset))
736 {
Matt Simmering58e379d2022-09-23 14:45:50 -0700737#if IGNORE_SOFT_RESETS_DURING_POST
738 if (ignoreNextSoftReset)
739 {
740 ignoreNextSoftReset = false;
741 return;
742 }
743#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700744 restartCause = getRestartCause(RestartCause::softReset);
745 }
746
747 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700748}
749
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700750static void systemPowerGoodFailedLog()
751{
752 sd_journal_send(
753 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
754 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
755 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
Jason M. Billsaeefe042021-09-08 14:56:11 -0700756 TimerMap["SioPowerGoodWatchdogMs"], NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700757}
758
Jason M. Bills35471132025-05-06 12:26:10 -0700759static void powerOKFailedLog()
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700760{
Jason M. Bills35471132025-05-06 12:26:10 -0700761 sd_journal_send("MESSAGE=PowerControl: power okay failed to assert",
762 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
763 "OpenBMC.0.1.SystemPowerOnFailed", NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700764}
765
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700766static void powerRestorePolicyLog()
767{
768 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
769 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
770 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
771}
772
773static void powerButtonPressLog()
774{
775 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
776 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
777 "OpenBMC.0.1.PowerButtonPressed", NULL);
778}
779
780static void resetButtonPressLog()
781{
782 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
783 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
784 "OpenBMC.0.1.ResetButtonPressed", NULL);
785}
786
787static void nmiButtonPressLog()
788{
789 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
790 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
791 "OpenBMC.0.1.NMIButtonPressed", NULL);
792}
793
794static void nmiDiagIntLog()
795{
796 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
797 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
798 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
799}
800
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300801PersistentState::PersistentState()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700802{
803 // create the power control directory if it doesn't exist
804 std::error_code ec;
805 if (!(std::filesystem::create_directories(powerControlDir, ec)))
806 {
807 if (ec.value() != 0)
808 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800809 lg2::error("failed to create {DIR_NAME}: {ERROR_MSG}", "DIR_NAME",
810 powerControlDir.string(), "ERROR_MSG", ec.message());
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300811 throw std::runtime_error("Failed to create state directory");
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700812 }
813 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300814
815 // read saved state, it's ok, if the file doesn't exists
816 std::ifstream appStateStream(powerControlDir / stateFile);
817 if (!appStateStream.is_open())
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700818 {
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300819 lg2::info("Cannot open state file \'{PATH}\'", "PATH",
820 std::string(powerControlDir / stateFile));
821 stateData = nlohmann::json({});
822 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700823 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300824 try
825 {
826 appStateStream >> stateData;
827 if (stateData.is_discarded())
828 {
829 lg2::info("Cannot parse state file \'{PATH}\'", "PATH",
830 std::string(powerControlDir / stateFile));
831 stateData = nlohmann::json({});
832 return;
833 }
834 }
835 catch (const std::exception& ex)
836 {
837 lg2::info("Cannot read state file \'{PATH}\'", "PATH",
838 std::string(powerControlDir / stateFile));
839 stateData = nlohmann::json({});
840 return;
841 }
842}
843PersistentState::~PersistentState()
844{
845 saveState();
846}
847const std::string PersistentState::get(Params parameter)
848{
849 auto val = stateData.find(getName(parameter));
850 if (val != stateData.end())
851 {
852 return val->get<std::string>();
853 }
854 return getDefault(parameter);
855}
856void PersistentState::set(Params parameter, const std::string& value)
857{
858 stateData[getName(parameter)] = value;
859 saveState();
860}
861
862const std::string PersistentState::getName(const Params parameter)
863{
864 switch (parameter)
865 {
866 case Params::PowerState:
867 return "PowerState";
868 }
869 return "";
870}
871const std::string PersistentState::getDefault(const Params parameter)
872{
873 switch (parameter)
874 {
875 case Params::PowerState:
876 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
877 }
878 return "";
879}
880void PersistentState::saveState()
881{
882 std::ofstream appStateStream(powerControlDir / stateFile, std::ios::trunc);
883 if (!appStateStream.is_open())
884 {
885 lg2::error("Cannot write state file \'{PATH}\'", "PATH",
886 std::string(powerControlDir / stateFile));
887 return;
888 }
889 appStateStream << stateData.dump(indentationSize);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700890}
891
Patrick Williams48aa1f02023-05-10 07:50:30 -0500892static constexpr const char* setingsService = "xyz.openbmc_project.Settings";
893static constexpr const char* powerRestorePolicyIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300894 "xyz.openbmc_project.Control.Power.RestorePolicy";
895#ifdef USE_ACBOOT
Patrick Williams48aa1f02023-05-10 07:50:30 -0500896static constexpr const char* powerACBootObject =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300897 "/xyz/openbmc_project/control/host0/ac_boot";
Patrick Williams48aa1f02023-05-10 07:50:30 -0500898static constexpr const char* powerACBootIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300899 "xyz.openbmc_project.Common.ACBoot";
900#endif // USE_ACBOOT
901
902namespace match_rules = sdbusplus::bus::match::rules;
903
904static int powerRestoreConfigHandler(sd_bus_message* m, void* context,
905 sd_bus_error*)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700906{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300907 if (context == nullptr || m == nullptr)
908 {
909 throw std::runtime_error("Invalid match");
910 }
Patrick Williams439b9c32022-07-22 19:26:53 -0500911 sdbusplus::message_t message(m);
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300912 PowerRestoreController* powerRestore =
913 static_cast<PowerRestoreController*>(context);
914
915 if (std::string(message.get_member()) == "InterfacesAdded")
916 {
917 sdbusplus::message::object_path path;
918 boost::container::flat_map<std::string, dbusPropertiesList> data;
919
920 message.read(path, data);
921
922 for (auto& [iface, properties] : data)
923 {
924 if ((iface == powerRestorePolicyIface)
925#ifdef USE_ACBOOT
926 || (iface == powerACBootIface)
927#endif // USE_ACBOOT
928 )
929 {
930 powerRestore->setProperties(properties);
931 }
932 }
933 }
934 else if (std::string(message.get_member()) == "PropertiesChanged")
935 {
936 std::string interfaceName;
937 dbusPropertiesList propertiesChanged;
938
939 message.read(interfaceName, propertiesChanged);
940
941 powerRestore->setProperties(propertiesChanged);
942 }
943 return 1;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700944}
945
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300946void PowerRestoreController::run()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700947{
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400948 std::string powerRestorePolicyObject =
949 "/xyz/openbmc_project/control/host" + node + "/power_restore_policy";
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300950 powerRestorePolicyLog();
951 // this list only needs to be created once
952 if (matches.empty())
953 {
954 matches.emplace_back(
955 *conn,
956 match_rules::interfacesAdded() +
957 match_rules::argNpath(0, powerRestorePolicyObject) +
958 match_rules::sender(setingsService),
959 powerRestoreConfigHandler, this);
960#ifdef USE_ACBOOT
961 matches.emplace_back(*conn,
962 match_rules::interfacesAdded() +
963 match_rules::argNpath(0, powerACBootObject) +
964 match_rules::sender(setingsService),
965 powerRestoreConfigHandler, this);
966 matches.emplace_back(*conn,
967 match_rules::propertiesChanged(powerACBootObject,
968 powerACBootIface) +
969 match_rules::sender(setingsService),
970 powerRestoreConfigHandler, this);
971#endif // USE_ACBOOT
972 }
973
974 // Check if it's already on DBus
975 conn->async_method_call(
976 [this](boost::system::error_code ec,
977 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400978 if (ec)
979 {
980 return;
981 }
982 setProperties(properties);
983 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300984 setingsService, powerRestorePolicyObject,
985 "org.freedesktop.DBus.Properties", "GetAll", powerRestorePolicyIface);
986
987#ifdef USE_ACBOOT
988 // Check if it's already on DBus
989 conn->async_method_call(
990 [this](boost::system::error_code ec,
991 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400992 if (ec)
993 {
994 return;
995 }
996 setProperties(properties);
997 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300998 setingsService, powerACBootObject, "org.freedesktop.DBus.Properties",
999 "GetAll", powerACBootIface);
1000#endif
1001}
1002
1003void PowerRestoreController::setProperties(const dbusPropertiesList& props)
1004{
1005 for (auto& [property, propValue] : props)
1006 {
1007 if (property == "PowerRestorePolicy")
1008 {
1009 const std::string* value = std::get_if<std::string>(&propValue);
1010 if (value == nullptr)
1011 {
1012 lg2::error("Unable to read Power Restore Policy");
1013 continue;
1014 }
1015 powerRestorePolicy = *value;
1016 }
1017 else if (property == "PowerRestoreDelay")
1018 {
1019 const uint64_t* value = std::get_if<uint64_t>(&propValue);
1020 if (value == nullptr)
1021 {
1022 lg2::error("Unable to read Power Restore Delay");
1023 continue;
1024 }
1025 powerRestoreDelay = *value / 1000000; // usec to sec
1026 }
1027#ifdef USE_ACBOOT
1028 else if (property == "ACBoot")
1029 {
1030 const std::string* value = std::get_if<std::string>(&propValue);
1031 if (value == nullptr)
1032 {
1033 lg2::error("Unable to read AC Boot status");
1034 continue;
1035 }
1036 acBoot = *value;
1037 }
1038#endif // USE_ACBOOT
1039 }
1040 invokeIfReady();
1041}
1042
1043void PowerRestoreController::invokeIfReady()
1044{
1045 if ((powerRestorePolicy.empty()) || (powerRestoreDelay < 0))
1046 {
1047 return;
1048 }
1049#ifdef USE_ACBOOT
1050 if (acBoot.empty() || acBoot == "Unknown")
1051 {
1052 return;
1053 }
1054#endif
1055
1056 matches.clear();
1057 if (!timerFired)
1058 {
1059 // Calculate the delay from now to meet the requested delay
1060 // Subtract the approximate uboot time
1061 static constexpr const int ubootSeconds = 20;
1062 int delay = powerRestoreDelay - ubootSeconds;
1063 // Subtract the time since boot
1064 struct sysinfo info = {};
1065 if (sysinfo(&info) == 0)
1066 {
1067 delay -= info.uptime;
1068 }
1069
1070 if (delay > 0)
1071 {
1072 powerRestoreTimer.expires_after(std::chrono::seconds(delay));
1073 lg2::info("Power Restore delay of {DELAY} seconds started", "DELAY",
1074 delay);
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001075 powerRestoreTimer.async_wait([this](const boost::system::error_code
1076 ec) {
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001077 if (ec)
1078 {
1079 // operation_aborted is expected if timer is canceled before
1080 // completion.
1081 if (ec == boost::asio::error::operation_aborted)
1082 {
1083 return;
1084 }
1085 lg2::error(
1086 "power restore policy async_wait failed: {ERROR_MSG}",
1087 "ERROR_MSG", ec.message());
1088 }
1089 else
1090 {
1091 lg2::info("Power Restore delay timer expired");
1092 }
1093 invoke();
1094 });
1095 timerFired = true;
1096 }
1097 else
1098 {
1099 invoke();
1100 }
1101 }
1102}
1103
1104void PowerRestoreController::invoke()
1105{
1106 // we want to run Power Restore only once
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001107 if (policyInvoked)
1108 {
1109 return;
1110 }
1111 policyInvoked = true;
1112
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001113 lg2::info("Invoking Power Restore Policy {POLICY}", "POLICY",
1114 powerRestorePolicy);
1115 if (powerRestorePolicy ==
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001116 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
1117 {
1118 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001119 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001120 }
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001121 else if (powerRestorePolicy ==
Jason M. Bills418ce112021-09-08 15:15:05 -07001122 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001123 {
1124 if (wasPowerDropped())
1125 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001126 lg2::info("Power was dropped, restoring Host On state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001127 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001128 setRestartCauseProperty(
1129 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001130 }
1131 else
1132 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001133 lg2::info("No power drop, restoring Host Off state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001134 }
1135 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -07001136 // We're done with the previous power state for the restore policy, so store
1137 // the current state
1138 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001139}
1140
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001141bool PowerRestoreController::wasPowerDropped()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001142{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001143 std::string state = appState.get(PersistentState::Params::PowerState);
1144 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001145}
1146
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001147static void waitForGPIOEvent(
1148 const std::string& name, const std::function<void(bool)>& eventHandler,
1149 gpiod::line& line, boost::asio::posix::stream_descriptor& event)
Zev Weiss676ef2c2021-09-02 21:54:02 -05001150{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001151 event.async_wait(
1152 boost::asio::posix::stream_descriptor::wait_read,
1153 [&name, eventHandler, &line,
1154 &event](const boost::system::error_code ec) {
1155 if (ec)
1156 {
1157 lg2::error("{GPIO_NAME} fd handler error: {ERROR_MSG}",
1158 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1159 // TODO: throw here to force power-control to
1160 // restart?
1161 return;
1162 }
1163 gpiod::line_event line_event = line.event_read();
1164 eventHandler(line_event.event_type ==
1165 gpiod::line_event::RISING_EDGE);
1166 waitForGPIOEvent(name, eventHandler, line, event);
1167 });
Zev Weiss676ef2c2021-09-02 21:54:02 -05001168}
1169
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001170static bool requestGPIOEvents(
Zev Weiss676ef2c2021-09-02 21:54:02 -05001171 const std::string& name, const std::function<void(bool)>& handler,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001172 gpiod::line& gpioLine,
1173 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1174{
1175 // Find the GPIO line
1176 gpioLine = gpiod::find_line(name);
1177 if (!gpioLine)
1178 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001179 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001180 return false;
1181 }
1182
1183 try
1184 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001185 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001186 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001187 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001188 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001189 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}",
1190 "GPIO_NAME", name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001191 return false;
1192 }
1193
1194 int gpioLineFd = gpioLine.event_get_fd();
1195 if (gpioLineFd < 0)
1196 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001197 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001198 return false;
1199 }
1200
1201 gpioEventDescriptor.assign(gpioLineFd);
1202
Zev Weiss676ef2c2021-09-02 21:54:02 -05001203 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001204 return true;
1205}
1206
1207static bool setGPIOOutput(const std::string& name, const int value,
1208 gpiod::line& gpioLine)
1209{
1210 // Find the GPIO line
1211 gpioLine = gpiod::find_line(name);
1212 if (!gpioLine)
1213 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001214 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001215 return false;
1216 }
1217
1218 // Request GPIO output to specified value
1219 try
1220 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001221 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}},
1222 value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001223 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001224 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001225 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001226 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
1227 name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001228 return false;
1229 }
1230
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001231 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1232 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001233 return true;
1234}
1235
1236static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1237 const std::string& name, const int value,
1238 const int durationMs)
1239{
1240 // Set the masked GPIO line to the specified value
1241 maskedGPIOLine.set_value(value);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001242 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1243 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001244 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001245 gpioAssertTimer.async_wait(
1246 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001247 // Set the masked GPIO line back to the opposite value
1248 maskedGPIOLine.set_value(!value);
1249 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1250 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001251 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001252 // operation_aborted is expected if timer is canceled before
1253 // completion.
1254 if (ec != boost::asio::error::operation_aborted)
1255 {
1256 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1257 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1258 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001259 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001260 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001261 return 0;
1262}
1263
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001264static int setGPIOOutputForMs(const ConfigData& config, const int value,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001265 const int durationMs)
1266{
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001267 // If the requested GPIO is masked, use the mask line to set the output
1268 if (powerButtonMask && config.lineName == powerOutConfig.lineName)
1269 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001270 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value,
1271 durationMs);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001272 }
1273 if (resetButtonMask && config.lineName == resetOutConfig.lineName)
1274 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001275 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value,
1276 durationMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001277 }
1278
1279 // No mask set, so request and set the GPIO normally
1280 gpiod::line gpioLine;
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001281 if (!setGPIOOutput(config.lineName, value, gpioLine))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001282 {
1283 return -1;
1284 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001285 const std::string name = config.lineName;
Jean-Marie Verdun2c495cf2021-09-17 21:42:23 -04001286
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001287 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001288 gpioAssertTimer.async_wait(
1289 [gpioLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001290 // Set the GPIO line back to the opposite value
1291 gpioLine.set_value(!value);
1292 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1293 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001294 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001295 // operation_aborted is expected if timer is canceled before
1296 // completion.
1297 if (ec != boost::asio::error::operation_aborted)
1298 {
1299 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1300 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1301 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001302 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001303 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001304 return 0;
1305}
1306
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001307static int assertGPIOForMs(const ConfigData& config, const int durationMs)
1308{
1309 return setGPIOOutputForMs(config, config.polarity, durationMs);
1310}
1311
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001312static void powerOn()
1313{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001314 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001315}
Naveen Moses117c34e2021-05-26 20:10:51 +05301316#ifdef CHASSIS_SYSTEM_RESET
1317static int slotPowerOn()
1318{
1319 if (power_control::slotPowerState != power_control::SlotPowerState::on)
1320 {
Naveen Moses117c34e2021-05-26 20:10:51 +05301321 slotPowerLine.set_value(1);
1322
1323 if (slotPowerLine.get_value() > 0)
1324 {
1325 setSlotPowerState(SlotPowerState::on);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001326 lg2::info("Slot Power is switched On\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301327 }
1328 else
1329 {
1330 return -1;
1331 }
1332 }
1333 else
1334 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001335 lg2::info("Slot Power is already in 'On' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301336 return -1;
1337 }
1338 return 0;
1339}
1340static int slotPowerOff()
1341{
1342 if (power_control::slotPowerState != power_control::SlotPowerState::off)
1343 {
1344 slotPowerLine.set_value(0);
1345
1346 if (!(slotPowerLine.get_value() > 0))
1347 {
1348 setSlotPowerState(SlotPowerState::off);
1349 setPowerState(PowerState::off);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001350 lg2::info("Slot Power is switched Off\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301351 }
1352 else
1353 {
1354 return -1;
1355 }
1356 }
1357 else
1358 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001359 lg2::info("Slot Power is already in 'Off' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301360 return -1;
1361 }
1362 return 0;
1363}
1364static void slotPowerCycle()
1365{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001366 lg2::info("Slot Power Cycle started\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301367 slotPowerOff();
1368 slotPowerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001369 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"]));
Naveen Moses117c34e2021-05-26 20:10:51 +05301370 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) {
1371 if (ec)
1372 {
1373 if (ec != boost::asio::error::operation_aborted)
1374 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001375 lg2::error(
1376 "Slot Power cycle timer async_wait failed: {ERROR_MSG}",
1377 "ERROR_MSG", ec.message());
Naveen Moses117c34e2021-05-26 20:10:51 +05301378 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001379 lg2::info("Slot Power cycle timer canceled\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301380 return;
1381 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001382 lg2::info("Slot Power cycle timer completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301383 slotPowerOn();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001384 lg2::info("Slot Power Cycle Completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301385 });
1386}
1387#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001388static void gracefulPowerOff()
1389{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001390 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001391}
1392
1393static void forcePowerOff()
1394{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001395 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001396 {
1397 return;
1398 }
1399
Jason M. Billsc6961b62021-10-21 14:08:02 -07001400 // If the force off timer expires, then the power-button override failed
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001401 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1402 if (ec)
1403 {
1404 // operation_aborted is expected if timer is canceled before
1405 // completion.
1406 if (ec != boost::asio::error::operation_aborted)
1407 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001408 lg2::error("Force power off async_wait failed: {ERROR_MSG}",
1409 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001410 }
1411 return;
1412 }
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001413
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001414 lg2::error("Power-button override failed. Not sure what to do now.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001415 });
1416}
1417
1418static void reset()
1419{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001420 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001421}
1422
1423static void gracefulPowerOffTimerStart()
1424{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001425 lg2::info("Graceful power-off timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001426 gracefulPowerOffTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001427 std::chrono::seconds(TimerMap["GracefulPowerOffS"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001428 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1429 if (ec)
1430 {
1431 // operation_aborted is expected if timer is canceled before
1432 // completion.
1433 if (ec != boost::asio::error::operation_aborted)
1434 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001435 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}",
1436 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001437 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001438 lg2::info("Graceful power-off timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001439 return;
1440 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001441 lg2::info("Graceful power-off timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001442 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1443 });
1444}
1445
1446static void powerCycleTimerStart()
1447{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001448 lg2::info("Power-cycle timer started");
Priyatharshan P70120512020-09-16 18:47:20 +05301449 powerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001450 std::chrono::milliseconds(TimerMap["PowerCycleMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001451 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1452 if (ec)
1453 {
1454 // operation_aborted is expected if timer is canceled before
1455 // completion.
1456 if (ec != boost::asio::error::operation_aborted)
1457 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001458 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}",
1459 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001460 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001461 lg2::info("Power-cycle timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001462 return;
1463 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001464 lg2::info("Power-cycle timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001465 sendPowerControlEvent(Event::powerCycleTimerExpired);
1466 });
1467}
1468
Jason M. Bills35471132025-05-06 12:26:10 -07001469static void powerOKWatchdogTimerStart()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001470{
Jason M. Bills35471132025-05-06 12:26:10 -07001471 lg2::info("power OK watchdog timer started");
1472 powerOKWatchdogTimer.expires_after(
1473 std::chrono::milliseconds(TimerMap["PowerOKWatchdogMs"]));
1474 powerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) {
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001475 if (ec)
1476 {
1477 // operation_aborted is expected if timer is canceled before
1478 // completion.
1479 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001480 {
Jason M. Bills35471132025-05-06 12:26:10 -07001481 lg2::error("power OK watchdog async_wait failed: {ERROR_MSG}",
1482 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001483 }
Jason M. Bills35471132025-05-06 12:26:10 -07001484 lg2::info("power OK watchdog timer canceled");
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001485 return;
1486 }
Jason M. Bills35471132025-05-06 12:26:10 -07001487 lg2::info("power OK watchdog timer expired");
1488 sendPowerControlEvent(Event::powerOKWatchdogTimerExpired);
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001489 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001490}
1491
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001492static void warmResetCheckTimerStart()
1493{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001494 lg2::info("Warm reset check timer started");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001495 warmResetCheckTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001496 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"]));
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001497 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1498 if (ec)
1499 {
1500 // operation_aborted is expected if timer is canceled before
1501 // completion.
1502 if (ec != boost::asio::error::operation_aborted)
1503 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001504 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}",
1505 "ERROR_MSG", ec.message());
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001506 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001507 lg2::info("Warm reset check timer canceled");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001508 return;
1509 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001510 lg2::info("Warm reset check timer completed");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001511 sendPowerControlEvent(Event::warmResetDetected);
1512 });
1513}
1514
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001515static void pohCounterTimerStart()
1516{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001517 lg2::info("POH timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001518 // Set the time-out as 1 hour, to align with POH command in ipmid
1519 pohCounterTimer.expires_after(std::chrono::hours(1));
1520 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1521 if (ec)
1522 {
1523 // operation_aborted is expected if timer is canceled before
1524 // completion.
1525 if (ec != boost::asio::error::operation_aborted)
1526 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001527 lg2::error("POH timer async_wait failed: {ERROR_MSG}",
1528 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001529 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001530 lg2::info("POH timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001531 return;
1532 }
1533
1534 if (getHostState(powerState) !=
1535 "xyz.openbmc_project.State.Host.HostState.Running")
1536 {
1537 return;
1538 }
1539
1540 conn->async_method_call(
1541 [](boost::system::error_code ec,
1542 const std::variant<uint32_t>& pohCounterProperty) {
1543 if (ec)
1544 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001545 lg2::error("error getting poh counter");
1546 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001547 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001548 const uint32_t* pohCounter =
1549 std::get_if<uint32_t>(&pohCounterProperty);
1550 if (pohCounter == nullptr)
1551 {
1552 lg2::error("unable to read poh counter");
1553 return;
1554 }
1555
1556 conn->async_method_call(
1557 [](boost::system::error_code ec) {
1558 if (ec)
1559 {
1560 lg2::error("failed to set poh counter");
1561 }
1562 },
1563 "xyz.openbmc_project.Settings",
1564 "/xyz/openbmc_project/state/chassis0",
1565 "org.freedesktop.DBus.Properties", "Set",
1566 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1567 std::variant<uint32_t>(*pohCounter + 1));
Patrick Williamsd394c882023-10-20 11:18:44 -05001568 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001569 "xyz.openbmc_project.Settings",
1570 "/xyz/openbmc_project/state/chassis0",
1571 "org.freedesktop.DBus.Properties", "Get",
1572 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1573
1574 pohCounterTimerStart();
1575 });
1576}
1577
1578static void currentHostStateMonitor()
1579{
Yong Li8d660212019-12-27 10:18:10 +08001580 if (getHostState(powerState) ==
1581 "xyz.openbmc_project.State.Host.HostState.Running")
1582 {
1583 pohCounterTimerStart();
1584 // Clear the restart cause set for the next restart
1585 clearRestartCause();
1586 }
1587 else
1588 {
1589 pohCounterTimer.cancel();
1590 // Set the restart cause set for this restart
1591 setRestartCause();
1592 }
1593
Jayanth Othayoth857c5582025-06-12 11:17:51 -05001594 std::string objectPath = "/xyz/openbmc_project/state/host" + node;
1595
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001596 static auto match = sdbusplus::bus::match_t(
1597 *conn,
1598 "type='signal',member='PropertiesChanged', "
1599 "interface='org.freedesktop.DBus.Properties', "
Jayanth Othayoth857c5582025-06-12 11:17:51 -05001600 "path='" +
1601 objectPath +
1602 "',"
1603 "arg0='xyz.openbmc_project.State.Host'",
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001604 [](sdbusplus::message_t& message) {
1605 std::string intfName;
1606 std::map<std::string, std::variant<std::string>> properties;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001607
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001608 try
1609 {
1610 message.read(intfName, properties);
1611 }
1612 catch (const std::exception& e)
1613 {
1614 lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1615 return;
1616 }
1617 if (properties.empty())
1618 {
1619 lg2::error("ERROR: Empty PropertiesChanged signal received");
1620 return;
1621 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001622
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001623 // We only want to check for CurrentHostState
1624 if (properties.begin()->first != "CurrentHostState")
1625 {
1626 return;
1627 }
1628 std::string* currentHostState =
1629 std::get_if<std::string>(&(properties.begin()->second));
1630 if (currentHostState == nullptr)
1631 {
1632 lg2::error("{PROPERTY} property invalid", "PROPERTY",
1633 properties.begin()->first);
1634 return;
1635 }
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001636
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001637 if (*currentHostState ==
1638 "xyz.openbmc_project.State.Host.HostState.Running")
1639 {
1640 pohCounterTimerStart();
1641 // Clear the restart cause set for the next restart
1642 clearRestartCause();
1643 sd_journal_send("MESSAGE=Host system DC power is on",
1644 "PRIORITY=%i", LOG_INFO,
1645 "REDFISH_MESSAGE_ID=%s",
1646 "OpenBMC.0.1.DCPowerOn", NULL);
1647 }
1648 else
1649 {
1650 pohCounterTimer.cancel();
1651 // POST_COMPLETE GPIO event is not working in some platforms
1652 // when power state is changed to OFF. This resulted in
1653 // 'OperatingSystemState' to stay at 'Standby', even though
1654 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1655 // if HostState is trurned to OFF.
1656 setOperatingSystemState(OperatingSystemStateStage::Inactive);
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301657
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001658 // Set the restart cause set for this restart
1659 setRestartCause();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001660#ifdef USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001661 resetACBootProperty();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001662#endif // USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001663 sd_journal_send("MESSAGE=Host system DC power is off",
1664 "PRIORITY=%i", LOG_INFO,
1665 "REDFISH_MESSAGE_ID=%s",
1666 "OpenBMC.0.1.DCPowerOff", NULL);
1667 }
1668 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001669}
1670
1671static void sioPowerGoodWatchdogTimerStart()
1672{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001673 lg2::info("SIO power good watchdog timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001674 sioPowerGoodWatchdogTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001675 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001676 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code
1677 ec) {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001678 if (ec)
1679 {
1680 // operation_aborted is expected if timer is canceled before
1681 // completion.
1682 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001683 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001684 lg2::error(
1685 "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1686 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001687 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001688 lg2::info("SIO power good watchdog timer canceled");
1689 return;
1690 }
1691 lg2::info("SIO power good watchdog timer completed");
1692 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1693 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001694}
1695
1696static void powerStateOn(const Event event)
1697{
1698 logEvent(__FUNCTION__, event);
1699 switch (event)
1700 {
Jason M. Bills35471132025-05-06 12:26:10 -07001701 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001702 setPowerState(PowerState::off);
1703 // DC power is unexpectedly lost, beep
1704 beep(beepPowerFail);
1705 break;
1706 case Event::sioS5Assert:
1707 setPowerState(PowerState::transitionToOff);
Matt Simmering58e379d2022-09-23 14:45:50 -07001708#if IGNORE_SOFT_RESETS_DURING_POST
1709 // Only recognize soft resets once host gets past POST COMPLETE
1710 if (operatingSystemState != OperatingSystemStateStage::Standby)
1711 {
1712 ignoreNextSoftReset = true;
1713 }
1714#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001715 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001716 break;
Jason M. Billsfb957332021-01-28 13:18:46 -08001717#if USE_PLT_RST
1718 case Event::pltRstAssert:
1719#else
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001720 case Event::postCompleteDeAssert:
Jason M. Billsfb957332021-01-28 13:18:46 -08001721#endif
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001722 setPowerState(PowerState::checkForWarmReset);
Matt Simmering58e379d2022-09-23 14:45:50 -07001723#if IGNORE_SOFT_RESETS_DURING_POST
1724 // Only recognize soft resets once host gets past POST COMPLETE
1725 if (operatingSystemState != OperatingSystemStateStage::Standby)
1726 {
1727 ignoreNextSoftReset = true;
1728 }
1729#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001730 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001731 warmResetCheckTimerStart();
1732 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001733 case Event::powerButtonPressed:
1734 setPowerState(PowerState::gracefulTransitionToOff);
1735 gracefulPowerOffTimerStart();
1736 break;
1737 case Event::powerOffRequest:
1738 setPowerState(PowerState::transitionToOff);
1739 forcePowerOff();
1740 break;
1741 case Event::gracefulPowerOffRequest:
1742 setPowerState(PowerState::gracefulTransitionToOff);
1743 gracefulPowerOffTimerStart();
1744 gracefulPowerOff();
1745 break;
1746 case Event::powerCycleRequest:
1747 setPowerState(PowerState::transitionToCycleOff);
1748 forcePowerOff();
1749 break;
1750 case Event::gracefulPowerCycleRequest:
1751 setPowerState(PowerState::gracefulTransitionToCycleOff);
1752 gracefulPowerOffTimerStart();
1753 gracefulPowerOff();
1754 break;
Jayanth Othayothdc0bab92024-02-07 07:24:35 -06001755 case Event::resetButtonPressed:
1756 setPowerState(PowerState::checkForWarmReset);
1757 warmResetCheckTimerStart();
1758 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001759 case Event::resetRequest:
1760 reset();
1761 break;
1762 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001763 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001764 break;
1765 }
1766}
1767
Jason M. Bills35471132025-05-06 12:26:10 -07001768static void powerStateWaitForPowerOK(const Event event)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001769{
1770 logEvent(__FUNCTION__, event);
1771 switch (event)
1772 {
Jason M. Bills35471132025-05-06 12:26:10 -07001773 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301774 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001775 // Cancel any GPIO assertions held during the transition
1776 gpioAssertTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001777 powerOKWatchdogTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301778 if (sioEnabled == true)
1779 {
1780 sioPowerGoodWatchdogTimerStart();
1781 setPowerState(PowerState::waitForSIOPowerGood);
1782 }
1783 else
1784 {
1785 setPowerState(PowerState::on);
1786 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001787 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301788 }
Jason M. Bills35471132025-05-06 12:26:10 -07001789 case Event::powerOKWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001790 setPowerState(PowerState::off);
Jason M. Bills35471132025-05-06 12:26:10 -07001791 powerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001792 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001793 case Event::sioPowerGoodAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001794 powerOKWatchdogTimer.cancel();
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001795 setPowerState(PowerState::on);
1796 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001797 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001798 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001799 break;
1800 }
1801}
1802
1803static void powerStateWaitForSIOPowerGood(const Event event)
1804{
1805 logEvent(__FUNCTION__, event);
1806 switch (event)
1807 {
1808 case Event::sioPowerGoodAssert:
1809 sioPowerGoodWatchdogTimer.cancel();
1810 setPowerState(PowerState::on);
1811 break;
1812 case Event::sioPowerGoodWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001813 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001814 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001815 break;
1816 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001817 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001818 break;
1819 }
1820}
1821
1822static void powerStateOff(const Event event)
1823{
1824 logEvent(__FUNCTION__, event);
1825 switch (event)
1826 {
Jason M. Bills35471132025-05-06 12:26:10 -07001827 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301828 {
1829 if (sioEnabled == true)
1830 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001831 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301832 setPowerState(PowerState::waitForSIOPowerGood);
1833 }
1834 else
1835 {
1836 setPowerState(PowerState::on);
1837 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001838 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301839 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001840 case Event::sioS5DeAssert:
Jason M. Bills35471132025-05-06 12:26:10 -07001841 powerOKWatchdogTimerStart();
1842 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001843 break;
Jason M. Bills273d7892020-06-17 14:46:57 -07001844 case Event::sioPowerGoodAssert:
1845 setPowerState(PowerState::on);
1846 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001847 case Event::powerButtonPressed:
Jason M. Bills35471132025-05-06 12:26:10 -07001848 powerOKWatchdogTimerStart();
1849 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001850 break;
1851 case Event::powerOnRequest:
Jason M. Bills35471132025-05-06 12:26:10 -07001852 powerOKWatchdogTimerStart();
1853 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001854 powerOn();
1855 break;
1856 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001857 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001858 break;
1859 }
1860}
1861
1862static void powerStateTransitionToOff(const Event event)
1863{
1864 logEvent(__FUNCTION__, event);
1865 switch (event)
1866 {
Jason M. Bills35471132025-05-06 12:26:10 -07001867 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001868 // Cancel any GPIO assertions held during the transition
1869 gpioAssertTimer.cancel();
1870 setPowerState(PowerState::off);
1871 break;
1872 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001873 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001874 break;
1875 }
1876}
1877
1878static void powerStateGracefulTransitionToOff(const Event event)
1879{
1880 logEvent(__FUNCTION__, event);
1881 switch (event)
1882 {
Jason M. Bills35471132025-05-06 12:26:10 -07001883 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001884 gracefulPowerOffTimer.cancel();
1885 setPowerState(PowerState::off);
1886 break;
1887 case Event::gracefulPowerOffTimerExpired:
1888 setPowerState(PowerState::on);
1889 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001890 case Event::powerOffRequest:
1891 gracefulPowerOffTimer.cancel();
1892 setPowerState(PowerState::transitionToOff);
1893 forcePowerOff();
1894 break;
1895 case Event::powerCycleRequest:
1896 gracefulPowerOffTimer.cancel();
1897 setPowerState(PowerState::transitionToCycleOff);
1898 forcePowerOff();
1899 break;
1900 case Event::resetRequest:
1901 gracefulPowerOffTimer.cancel();
1902 setPowerState(PowerState::on);
1903 reset();
1904 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001905 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001906 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001907 break;
1908 }
1909}
1910
1911static void powerStateCycleOff(const Event event)
1912{
1913 logEvent(__FUNCTION__, event);
1914 switch (event)
1915 {
Jason M. Bills35471132025-05-06 12:26:10 -07001916 case Event::powerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301917 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001918 powerCycleTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301919 if (sioEnabled == true)
1920 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001921 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301922 setPowerState(PowerState::waitForSIOPowerGood);
1923 }
1924 else
1925 {
1926 setPowerState(PowerState::on);
1927 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001928 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301929 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001930 case Event::sioS5DeAssert:
1931 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001932 powerOKWatchdogTimerStart();
1933 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001934 break;
1935 case Event::powerButtonPressed:
1936 powerCycleTimer.cancel();
Jason M. Bills35471132025-05-06 12:26:10 -07001937 powerOKWatchdogTimerStart();
1938 setPowerState(PowerState::waitForPowerOK);
Jason M. Bills35aa6652020-04-30 16:24:55 -07001939 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001940 case Event::powerCycleTimerExpired:
Jason M. Bills35471132025-05-06 12:26:10 -07001941 powerOKWatchdogTimerStart();
1942 setPowerState(PowerState::waitForPowerOK);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001943 powerOn();
1944 break;
1945 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001946 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001947 break;
1948 }
1949}
1950
1951static void powerStateTransitionToCycleOff(const Event event)
1952{
1953 logEvent(__FUNCTION__, event);
1954 switch (event)
1955 {
Jason M. Bills35471132025-05-06 12:26:10 -07001956 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001957 // Cancel any GPIO assertions held during the transition
1958 gpioAssertTimer.cancel();
1959 setPowerState(PowerState::cycleOff);
1960 powerCycleTimerStart();
1961 break;
1962 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001963 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001964 break;
1965 }
1966}
1967
1968static void powerStateGracefulTransitionToCycleOff(const Event event)
1969{
1970 logEvent(__FUNCTION__, event);
1971 switch (event)
1972 {
Jason M. Bills35471132025-05-06 12:26:10 -07001973 case Event::powerOKDeAssert:
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001974 gracefulPowerOffTimer.cancel();
1975 setPowerState(PowerState::cycleOff);
1976 powerCycleTimerStart();
1977 break;
1978 case Event::gracefulPowerOffTimerExpired:
1979 setPowerState(PowerState::on);
1980 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001981 case Event::powerOffRequest:
1982 gracefulPowerOffTimer.cancel();
1983 setPowerState(PowerState::transitionToOff);
1984 forcePowerOff();
1985 break;
1986 case Event::powerCycleRequest:
1987 gracefulPowerOffTimer.cancel();
1988 setPowerState(PowerState::transitionToCycleOff);
1989 forcePowerOff();
1990 break;
1991 case Event::resetRequest:
1992 gracefulPowerOffTimer.cancel();
1993 setPowerState(PowerState::on);
1994 reset();
1995 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001996 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001997 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001998 break;
1999 }
2000}
2001
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002002static void powerStateCheckForWarmReset(const Event event)
2003{
2004 logEvent(__FUNCTION__, event);
2005 switch (event)
2006 {
2007 case Event::sioS5Assert:
2008 warmResetCheckTimer.cancel();
2009 setPowerState(PowerState::transitionToOff);
2010 break;
2011 case Event::warmResetDetected:
2012 setPowerState(PowerState::on);
2013 break;
Jason M. Bills35471132025-05-06 12:26:10 -07002014 case Event::powerOKDeAssert:
P.K. Lee344dae82019-11-27 16:35:05 +08002015 warmResetCheckTimer.cancel();
2016 setPowerState(PowerState::off);
2017 // DC power is unexpectedly lost, beep
2018 beep(beepPowerFail);
2019 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002020 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002021 lg2::info("No action taken.");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002022 break;
2023 }
2024}
2025
Jason M. Bills35471132025-05-06 12:26:10 -07002026static void powerOKHandler(bool state)
Zev Weiss584aa132021-09-02 19:21:52 -05002027{
Zev Weissedc86f32024-05-07 01:44:33 +00002028 Event powerControlEvent = (state == powerOkConfig.polarity)
Jason M. Bills35471132025-05-06 12:26:10 -07002029 ? Event::powerOKAssert
2030 : Event::powerOKDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002031 sendPowerControlEvent(powerControlEvent);
2032}
2033
Zev Weiss584aa132021-09-02 19:21:52 -05002034static void sioPowerGoodHandler(bool state)
2035{
Zev Weissedc86f32024-05-07 01:44:33 +00002036 Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2037 ? Event::sioPowerGoodAssert
2038 : Event::sioPowerGoodDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002039 sendPowerControlEvent(powerControlEvent);
2040}
2041
Zev Weiss584aa132021-09-02 19:21:52 -05002042static void sioOnControlHandler(bool state)
2043{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002044 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2045 static_cast<int>(state));
Zev Weiss584aa132021-09-02 19:21:52 -05002046}
2047
Zev Weiss584aa132021-09-02 19:21:52 -05002048static void sioS5Handler(bool state)
2049{
Zev Weissedc86f32024-05-07 01:44:33 +00002050 Event powerControlEvent = (state == sioS5Config.polarity)
2051 ? Event::sioS5Assert
2052 : Event::sioS5DeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002053 sendPowerControlEvent(powerControlEvent);
2054}
2055
Zev Weiss584aa132021-09-02 19:21:52 -05002056static void powerButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002057{
Zev Weissedc86f32024-05-07 01:44:33 +00002058 bool asserted = state == powerButtonConfig.polarity;
2059 powerButtonIface->set_property("ButtonPressed", asserted);
2060 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002061 {
2062 powerButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002063 if (!powerButtonMask)
2064 {
2065 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002066 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002067 }
2068 else
2069 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002070 lg2::info("power button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002071 }
2072 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002073#if USE_BUTTON_PASSTHROUGH
2074 gpiod::line gpioLine;
2075 bool outputState =
2076 asserted ? powerOutConfig.polarity : (!powerOutConfig.polarity);
2077 if (!setGPIOOutput(powerOutConfig.lineName, outputState, gpioLine))
2078 {
2079 lg2::error("{GPIO_NAME} power button passthrough failed", "GPIO_NAME",
2080 powerOutConfig.lineName);
2081 }
2082#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002083}
2084
Zev Weiss584aa132021-09-02 19:21:52 -05002085static void resetButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002086{
Zev Weissedc86f32024-05-07 01:44:33 +00002087 bool asserted = state == resetButtonConfig.polarity;
2088 resetButtonIface->set_property("ButtonPressed", asserted);
2089 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002090 {
2091 resetButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002092 if (!resetButtonMask)
2093 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002094 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002095 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002096 }
2097 else
2098 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002099 lg2::info("reset button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002100 }
2101 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002102#if USE_BUTTON_PASSTHROUGH
2103 gpiod::line gpioLine;
2104 bool outputState =
2105 asserted ? resetOutConfig.polarity : (!resetOutConfig.polarity);
2106 if (!setGPIOOutput(resetOutConfig.lineName, outputState, gpioLine))
2107 {
2108 lg2::error("{GPIO_NAME} reset button passthrough failed", "GPIO_NAME",
2109 resetOutConfig.lineName);
2110 }
2111#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002112}
2113
Vijay Khemka04175c22020-10-09 14:28:11 -07002114#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002115static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2116static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2117static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2118static constexpr auto systemTargetName = "chassis-system-reset.target";
2119
2120void systemReset()
2121{
2122 conn->async_method_call(
2123 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002124 if (ec)
2125 {
2126 lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2127 ec.message());
2128 }
2129 },
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002130 systemdBusname, systemdPath, systemdInterface, "StartUnit",
2131 systemTargetName, "replace");
2132}
Vijay Khemka04175c22020-10-09 14:28:11 -07002133#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002134
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002135static void nmiSetEnableProperty(bool value)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002136{
2137 conn->async_method_call(
2138 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002139 if (ec)
2140 {
2141 lg2::error("failed to set NMI source");
2142 }
2143 },
Chen Yugang303bd582019-11-01 08:45:06 +08002144 "xyz.openbmc_project.Settings",
2145 "/xyz/openbmc_project/Chassis/Control/NMISource",
2146 "org.freedesktop.DBus.Properties", "Set",
2147 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2148 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002149}
2150
2151static void nmiReset(void)
2152{
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002153 const static constexpr int nmiOutPulseTimeMs = 200;
2154
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002155 lg2::info("NMI out action");
Jian Zhang461a1662022-09-22 11:29:01 +08002156 nmiOutLine.set_value(nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002157 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
Jian Zhang461a1662022-09-22 11:29:01 +08002158 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002159 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2160 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2161 // restore the NMI_OUT GPIO line back to the opposite value
Jian Zhang461a1662022-09-22 11:29:01 +08002162 nmiOutLine.set_value(!nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002163 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002164 if (ec)
2165 {
2166 // operation_aborted is expected if timer is canceled before
2167 // completion.
2168 if (ec != boost::asio::error::operation_aborted)
2169 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002170 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2171 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2172 ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002173 }
2174 }
2175 });
2176 // log to redfish
2177 nmiDiagIntLog();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002178 lg2::info("NMI out action completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002179 // reset Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002180 nmiSetEnableProperty(false);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002181}
2182
2183static void nmiSourcePropertyMonitor(void)
2184{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002185 lg2::info("NMI Source Property Monitor");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002186
Patrick Williams439b9c32022-07-22 19:26:53 -05002187 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2188 std::make_unique<sdbusplus::bus::match_t>(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002189 *conn,
2190 "type='signal',interface='org.freedesktop.DBus.Properties',"
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002191 "member='PropertiesChanged',"
2192 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
Patrick Williams439b9c32022-07-22 19:26:53 -05002193 [](sdbusplus::message_t& msg) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002194 std::string interfaceName;
2195 boost::container::flat_map<std::string,
2196 std::variant<bool, std::string>>
2197 propertiesChanged;
2198 std::string state;
2199 bool value = true;
2200 try
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002201 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002202 msg.read(interfaceName, propertiesChanged);
2203 if (propertiesChanged.begin()->first == "Enabled")
2204 {
2205 value =
2206 std::get<bool>(propertiesChanged.begin()->second);
2207 lg2::info(
2208 "NMI Enabled propertiesChanged value: {VALUE}",
2209 "VALUE", value);
2210 nmiEnabled = value;
2211 if (nmiEnabled)
2212 {
2213 nmiReset();
2214 }
2215 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002216 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002217 catch (const std::exception& e)
2218 {
2219 lg2::error("Unable to read NMI source: {ERROR}", "ERROR",
2220 e);
2221 return;
2222 }
2223 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002224}
2225
2226static void setNmiSource()
2227{
2228 conn->async_method_call(
2229 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002230 if (ec)
2231 {
2232 lg2::error("failed to set NMI source");
2233 }
2234 },
Chen Yugang303bd582019-11-01 08:45:06 +08002235 "xyz.openbmc_project.Settings",
2236 "/xyz/openbmc_project/Chassis/Control/NMISource",
2237 "org.freedesktop.DBus.Properties", "Set",
2238 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
Jason M. Bills418ce112021-09-08 15:15:05 -07002239 std::variant<std::string>{
Tim Lee6af569f2024-03-11 17:32:42 +08002240 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002241 // set Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002242 nmiSetEnableProperty(true);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002243}
2244
Zev Weiss584aa132021-09-02 19:21:52 -05002245static void nmiButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002246{
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002247 // Don't handle event if host not running and config doesn't force it
2248 if (!nmiWhenPoweredOff &&
2249 getHostState(powerState) !=
2250 "xyz.openbmc_project.State.Host.HostState.Running")
2251 {
2252 return;
2253 }
Zev Weissedc86f32024-05-07 01:44:33 +00002254
2255 bool asserted = state == nmiButtonConfig.polarity;
2256 nmiButtonIface->set_property("ButtonPressed", asserted);
2257 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002258 {
2259 nmiButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002260 if (nmiButtonMasked)
2261 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002262 lg2::info("NMI button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002263 }
2264 else
2265 {
2266 setNmiSource();
2267 }
2268 }
Zev Weiss584aa132021-09-02 19:21:52 -05002269}
2270
Zev Weiss584aa132021-09-02 19:21:52 -05002271static void idButtonHandler(bool state)
2272{
Zev Weissedc86f32024-05-07 01:44:33 +00002273 bool asserted = state == idButtonConfig.polarity;
2274 idButtonIface->set_property("ButtonPressed", asserted);
Zev Weiss584aa132021-09-02 19:21:52 -05002275}
2276
Jason M. Billsfb957332021-01-28 13:18:46 -08002277static void pltRstHandler(bool pltRst)
2278{
2279 if (pltRst)
2280 {
2281 sendPowerControlEvent(Event::pltRstDeAssert);
2282 }
2283 else
2284 {
2285 sendPowerControlEvent(Event::pltRstAssert);
2286 }
2287}
2288
Patrick Williams439b9c32022-07-22 19:26:53 -05002289[[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
Jason M. Billsfb957332021-01-28 13:18:46 -08002290{
2291 std::string interfaceName;
2292 boost::container::flat_map<std::string, std::variant<bool>>
2293 propertiesChanged;
Jason M. Billsfb957332021-01-28 13:18:46 -08002294 try
2295 {
2296 msg.read(interfaceName, propertiesChanged);
2297 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002298 catch (const std::exception& e)
Jason M. Billsfb957332021-01-28 13:18:46 -08002299 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002300 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
Jason M. Billsfb957332021-01-28 13:18:46 -08002301 return;
2302 }
2303 if (propertiesChanged.empty())
2304 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002305 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
Jason M. Billsfb957332021-01-28 13:18:46 -08002306 return;
2307 }
2308
2309 for (auto& [property, value] : propertiesChanged)
2310 {
2311 if (property == "ESpiPlatformReset")
2312 {
2313 bool* pltRst = std::get_if<bool>(&value);
2314 if (pltRst == nullptr)
2315 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002316 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
Jason M. Billsfb957332021-01-28 13:18:46 -08002317 return;
2318 }
2319 pltRstHandler(*pltRst);
2320 }
2321 }
2322}
2323
Zev Weiss584aa132021-09-02 19:21:52 -05002324static void postCompleteHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002325{
Zev Weissedc86f32024-05-07 01:44:33 +00002326 bool asserted = state == postCompleteConfig.polarity;
2327 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002328 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002329 sendPowerControlEvent(Event::postCompleteAssert);
Tim Lee86239182021-12-23 11:46:01 +08002330 setOperatingSystemState(OperatingSystemStateStage::Standby);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002331 }
2332 else
2333 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002334 sendPowerControlEvent(Event::postCompleteDeAssert);
Tim Lee86239182021-12-23 11:46:01 +08002335 setOperatingSystemState(OperatingSystemStateStage::Inactive);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002336 }
Zev Weiss584aa132021-09-02 19:21:52 -05002337}
2338
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302339static int loadConfigValues()
2340{
2341 const std::string configFilePath =
2342 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2343 ".json";
2344 std::ifstream configFile(configFilePath.c_str());
2345 if (!configFile.is_open())
2346 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002347 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2348 "PATH", configFilePath);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302349 return -1;
2350 }
Zev Weiss1aa08b22021-09-15 17:06:20 -05002351 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302352
Priyatharshan P70120512020-09-16 18:47:20 +05302353 if (jsonData.is_discarded())
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302354 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002355 lg2::error("Power config readings JSON parser failure");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302356 return -1;
2357 }
Priyatharshan P70120512020-09-16 18:47:20 +05302358 auto gpios = jsonData["gpio_configs"];
2359 auto timers = jsonData["timing_configs"];
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302360
Priyatharshan P70120512020-09-16 18:47:20 +05302361 ConfigData* tempGpioData;
2362
2363 for (nlohmann::json& gpioConfig : gpios)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302364 {
Priyatharshan P70120512020-09-16 18:47:20 +05302365 if (!gpioConfig.contains("Name"))
2366 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002367 lg2::error("The 'Name' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302368 return -1;
2369 }
2370
2371 // Iterate through the powersignal map to check if the gpio json config
2372 // entry is valid
2373 std::string gpioName = gpioConfig["Name"];
2374 auto signalMapIter = powerSignalMap.find(gpioName);
2375 if (signalMapIter == powerSignalMap.end())
2376 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002377 lg2::error(
2378 "{GPIO_NAME} is not a recognized power-control signal name",
2379 "GPIO_NAME", gpioName);
Priyatharshan P70120512020-09-16 18:47:20 +05302380 return -1;
2381 }
2382
2383 // assign the power signal name to the corresponding structure reference
2384 // from map then fillup the structure with coressponding json config
2385 // value
2386 tempGpioData = signalMapIter->second;
2387 tempGpioData->name = gpioName;
2388
2389 if (!gpioConfig.contains("Type"))
2390 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002391 lg2::error("The \'Type\' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302392 return -1;
2393 }
2394
2395 std::string signalType = gpioConfig["Type"];
2396 if (signalType == "GPIO")
2397 {
2398 tempGpioData->type = ConfigType::GPIO;
2399 }
2400 else if (signalType == "DBUS")
2401 {
2402 tempGpioData->type = ConfigType::DBUS;
2403 }
2404 else
2405 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002406 lg2::error("{TYPE} is not a recognized power-control signal type",
2407 "TYPE", signalType);
Priyatharshan P70120512020-09-16 18:47:20 +05302408 return -1;
2409 }
2410
2411 if (tempGpioData->type == ConfigType::GPIO)
2412 {
2413 if (gpioConfig.contains("LineName"))
2414 {
2415 tempGpioData->lineName = gpioConfig["LineName"];
2416 }
2417 else
2418 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002419 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002420 "The \'LineName\' field must be defined for GPIO configuration");
Priyatharshan P70120512020-09-16 18:47:20 +05302421 return -1;
2422 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002423 if (gpioConfig.contains("Polarity"))
2424 {
2425 std::string polarity = gpioConfig["Polarity"];
2426 if (polarity == "ActiveLow")
2427 {
2428 tempGpioData->polarity = false;
2429 }
2430 else if (polarity == "ActiveHigh")
2431 {
2432 tempGpioData->polarity = true;
2433 }
2434 else
2435 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002436 lg2::error(
2437 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2438 "POLARITY", polarity);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002439 return -1;
2440 }
2441 }
2442 else
2443 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002444 lg2::error("Polarity field not found for {GPIO_NAME}",
2445 "GPIO_NAME", tempGpioData->lineName);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002446 return -1;
2447 }
Priyatharshan P70120512020-09-16 18:47:20 +05302448 }
2449 else
2450 {
2451 // if dbus based gpio config is defined read and update the dbus
2452 // params corresponding to the gpio config instance
2453 for (auto& [key, dbusParamName] : dbusParams)
2454 {
Logananth Sundararaja4308042021-10-20 11:52:05 +05302455 if (!gpioConfig.contains(dbusParamName))
Priyatharshan P70120512020-09-16 18:47:20 +05302456 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002457 lg2::error(
2458 "The {DBUS_NAME} field must be defined for Dbus configuration ",
2459 "DBUS_NAME", dbusParamName);
Priyatharshan P70120512020-09-16 18:47:20 +05302460 return -1;
2461 }
2462 }
Logananth Sundararaja4308042021-10-20 11:52:05 +05302463 tempGpioData->dbusName =
2464 gpioConfig[dbusParams[DbusConfigType::name]];
2465 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
Priyatharshan P70120512020-09-16 18:47:20 +05302466 tempGpioData->interface =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302467 gpioConfig[dbusParams[DbusConfigType::interface]];
Priyatharshan P70120512020-09-16 18:47:20 +05302468 tempGpioData->lineName =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302469 gpioConfig[dbusParams[DbusConfigType::property]];
Zev Weissca478552024-06-11 23:45:58 +00002470
Zev Weissd603dd12024-06-19 21:50:52 +00002471 // dbus-based inputs must be active-high.
2472 tempGpioData->polarity = true;
2473
Zev Weissca478552024-06-11 23:45:58 +00002474 // MatchRegex is optional
2475 auto item = gpioConfig.find("MatchRegex");
2476 if (item != gpioConfig.end())
2477 {
2478 try
2479 {
2480 tempGpioData->matchRegex = std::regex(*item);
2481 }
2482 catch (const std::regex_error& e)
2483 {
2484 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2485 gpioName, "ERR", e.what());
2486 return -1;
2487 }
2488 }
Priyatharshan P70120512020-09-16 18:47:20 +05302489 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302490 }
2491
Priyatharshan P70120512020-09-16 18:47:20 +05302492 // read and store the timer values from json config to Timer Map
2493 for (auto& [key, timerValue] : TimerMap)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302494 {
Priyatharshan P70120512020-09-16 18:47:20 +05302495 if (timers.contains(key.c_str()))
2496 {
2497 timerValue = timers[key.c_str()];
2498 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302499 }
2500
Jonathan Doman891bbde2023-05-17 13:58:24 -07002501 // If "events_configs" key is not in json config, fallback to null
2502 auto events = jsonData.value("event_configs",
2503 nlohmann::json(nlohmann::json::value_t::null));
2504 if (events.is_object())
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002505 {
2506 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2507 }
2508
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302509 return 0;
2510}
Zev Weissa8f116a2021-09-01 21:08:30 -05002511
Zev Weissca478552024-06-11 23:45:58 +00002512template <typename T>
Patrick Williamsb84e7892025-02-01 08:22:06 -05002513static std::optional<T> getMessageValue(sdbusplus::message_t& msg,
2514 const std::string& name)
Zev Weissa8f116a2021-09-01 21:08:30 -05002515{
Zev Weissa8f116a2021-09-01 21:08:30 -05002516 std::string event;
Zev Weissca478552024-06-11 23:45:58 +00002517 std::string thresholdInterface;
2518 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2519
2520 msg.read(thresholdInterface, propertiesChanged);
2521 if (propertiesChanged.empty())
2522 {
2523 return std::nullopt;
2524 }
2525
2526 event = propertiesChanged.begin()->first;
2527 if (event.empty() || event != name)
2528 {
2529 return std::nullopt;
2530 }
2531
2532 return std::get<T>(propertiesChanged.begin()->second);
2533}
2534
2535static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2536 const ConfigData& config, bool& value)
2537{
Zev Weissa8f116a2021-09-01 21:08:30 -05002538 try
2539 {
Zev Weissca478552024-06-11 23:45:58 +00002540 if (config.matchRegex.has_value())
Zev Weissa8f116a2021-09-01 21:08:30 -05002541 {
Zev Weissca478552024-06-11 23:45:58 +00002542 std::optional<std::string> s =
2543 getMessageValue<std::string>(msg, config.lineName);
2544 if (!s.has_value())
2545 {
2546 return false;
2547 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002548
Zev Weissca478552024-06-11 23:45:58 +00002549 std::smatch m;
2550 value = std::regex_match(s.value(), m, config.matchRegex.value());
2551 }
2552 else
Zev Weissa8f116a2021-09-01 21:08:30 -05002553 {
Zev Weissca478552024-06-11 23:45:58 +00002554 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2555 if (!v.has_value())
2556 {
2557 return false;
2558 }
2559 value = v.value();
Zev Weissa8f116a2021-09-01 21:08:30 -05002560 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002561 return true;
2562 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002563 catch (const std::exception& e)
Zev Weissa8f116a2021-09-01 21:08:30 -05002564 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002565 lg2::error(
2566 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
Zev Weissca478552024-06-11 23:45:58 +00002567 "DBUS_NAME", config.lineName, "ERROR", e);
Zev Weissa8f116a2021-09-01 21:08:30 -05002568 return false;
2569 }
2570}
2571
Patrick Williamsb84e7892025-02-01 08:22:06 -05002572static sdbusplus::bus::match_t dbusGPIOMatcher(
2573 const ConfigData& cfg, std::function<void(bool)> onMatch)
Zev Weissa8f116a2021-09-01 21:08:30 -05002574{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002575 auto pulseEventMatcherCallback =
2576 [&cfg, onMatch](sdbusplus::message_t& msg) {
2577 bool value = false;
2578 if (!getDbusMsgGPIOState(msg, cfg, value))
2579 {
2580 return;
2581 }
2582 onMatch(value);
2583 };
Zev Weissa8f116a2021-09-01 21:08:30 -05002584
Patrick Williams439b9c32022-07-22 19:26:53 -05002585 return sdbusplus::bus::match_t(
2586 static_cast<sdbusplus::bus_t&>(*conn),
Zev Weissa8f116a2021-09-01 21:08:30 -05002587 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2588 "PropertiesChanged',arg0='" +
Zev Weiss1cc79212024-06-19 22:14:57 +00002589 cfg.interface + "',path='" + cfg.path + "',sender='" +
2590 cfg.dbusName + "'",
Zev Weissa8f116a2021-09-01 21:08:30 -05002591 std::move(pulseEventMatcherCallback));
2592}
2593
Michal Orzeledd211e2022-10-28 13:10:16 +02002594// D-Bus property read functions
2595void reschedulePropertyRead(const ConfigData& configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302596
Michal Orzeledd211e2022-10-28 13:10:16 +02002597int getProperty(const ConfigData& configData)
2598{
2599 std::variant<bool> resp;
2600
2601 try
Priyatharshan P70120512020-09-16 18:47:20 +05302602 {
Michal Orzeledd211e2022-10-28 13:10:16 +02002603 auto method = conn->new_method_call(
2604 configData.dbusName.c_str(), configData.path.c_str(),
2605 "org.freedesktop.DBus.Properties", "Get");
2606 method.append(configData.interface.c_str(),
2607 configData.lineName.c_str());
2608
2609 auto reply = conn->call(method);
2610 if (reply.is_method_error())
2611 {
2612 lg2::error(
2613 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
2614 "PROPERTY", configData.lineName, "INTERFACE",
2615 configData.interface, "PATH", configData.path);
2616 return -1;
2617 }
2618
2619 reply.read(resp);
2620 }
2621 catch (const sdbusplus::exception_t& e)
2622 {
2623 lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
2624 configData.lineName, "WHAT", e.what());
2625 reschedulePropertyRead(configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302626 return -1;
2627 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002628
Logananth Sundararaj85e111e2021-11-11 13:13:13 +05302629 auto respValue = std::get_if<bool>(&resp);
Priyatharshan P70120512020-09-16 18:47:20 +05302630 if (!respValue)
2631 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002632 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2633 "PROPERTY", configData.lineName);
Priyatharshan P70120512020-09-16 18:47:20 +05302634 return -1;
2635 }
2636 return (*respValue);
2637}
Michal Orzeledd211e2022-10-28 13:10:16 +02002638
2639void setInitialValue(const ConfigData& configData, bool initialValue)
2640{
2641 if (configData.name == "PowerOk")
2642 {
2643 powerState = (initialValue ? PowerState::on : PowerState::off);
2644 hostIface->set_property("CurrentHostState",
2645 std::string(getHostState(powerState)));
2646 }
2647 else if (configData.name == "PowerButton")
2648 {
2649 powerButtonIface->set_property("ButtonPressed", !initialValue);
2650 }
2651 else if (configData.name == "ResetButton")
2652 {
2653 resetButtonIface->set_property("ButtonPressed", !initialValue);
2654 }
2655 else if (configData.name == "NMIButton")
2656 {
2657 nmiButtonIface->set_property("ButtonPressed", !initialValue);
2658 }
2659 else if (configData.name == "IdButton")
2660 {
2661 idButtonIface->set_property("ButtonPressed", !initialValue);
2662 }
2663 else if (configData.name == "PostComplete")
2664 {
2665 OperatingSystemStateStage osState =
Jason M. Billsc6d75652024-09-10 16:54:26 -07002666 (initialValue == postCompleteConfig.polarity
2667 ? OperatingSystemStateStage::Standby
2668 : OperatingSystemStateStage::Inactive);
Michal Orzeledd211e2022-10-28 13:10:16 +02002669 setOperatingSystemState(osState);
2670 }
2671 else
2672 {
2673 lg2::error("Unknown name {NAME}", "NAME", configData.name);
2674 }
2675}
2676
2677void reschedulePropertyRead(const ConfigData& configData)
2678{
2679 auto item = dBusRetryTimers.find(configData.name);
2680
2681 if (item == dBusRetryTimers.end())
2682 {
2683 auto newItem = dBusRetryTimers.insert(
2684 {configData.name, boost::asio::steady_timer(io)});
2685
2686 if (!newItem.second)
2687 {
2688 lg2::error("Failed to add new timer for {NAME}", "NAME",
2689 configData.name);
2690 return;
2691 }
2692
2693 item = newItem.first;
2694 }
2695
2696 auto& timer = item->second;
2697 timer.expires_after(
2698 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2699 timer.async_wait([&configData](const boost::system::error_code ec) {
2700 if (ec)
2701 {
2702 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2703 configData.name, "MSG", ec.message());
2704 dBusRetryTimers.erase(configData.name);
2705 return;
2706 }
Willy Tu4d684112025-08-17 22:15:25 +00002707 sdbusplus::asio::getProperty<bool>(
2708 *conn, configData.dbusName, configData.path, configData.interface,
2709 configData.lineName,
2710 [&configData](boost::system::error_code ec, bool value) {
2711 if (ec)
2712 {
2713 lg2::error("Exception while reading {PROPERTY}: {WHAT}",
2714 "PROPERTY", configData.lineName, "WHAT",
2715 ec.message());
2716 reschedulePropertyRead(configData);
2717 return;
2718 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002719
Willy Tu4d684112025-08-17 22:15:25 +00002720 setInitialValue(configData, value);
2721 dBusRetryTimers.erase(configData.name);
2722 });
Michal Orzeledd211e2022-10-28 13:10:16 +02002723 });
2724}
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002725} // namespace power_control
2726
2727int main(int argc, char* argv[])
2728{
Lei YU92caa4c2021-02-23 16:59:25 +08002729 using namespace power_control;
Priyatharshan P70120512020-09-16 18:47:20 +05302730
2731 if (argc > 1)
2732 {
2733 node = argv[1];
2734 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002735 lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2736 node);
Priyatharshan P70120512020-09-16 18:47:20 +05302737
Lei YU92caa4c2021-02-23 16:59:25 +08002738 conn = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002739
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302740 // Load GPIO's through json config file
Lei YU92caa4c2021-02-23 16:59:25 +08002741 if (loadConfigValues() == -1)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302742 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002743 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302744 }
Naveen Mosesec972d82021-07-16 21:19:23 +05302745 /* Currently for single host based systems additional busname is added
2746 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2747 Going forward for single hosts the old bus name without zero numbering
2748 will be removed when all other applications adapted to the
2749 bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2750
2751 if (node == "0")
2752 {
2753 // Request all the dbus names
2754 conn->request_name(hostDbusName.c_str());
2755 conn->request_name(chassisDbusName.c_str());
2756 conn->request_name(osDbusName.c_str());
2757 conn->request_name(buttonDbusName.c_str());
2758 conn->request_name(nmiDbusName.c_str());
2759 conn->request_name(rstCauseDbusName.c_str());
2760 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302761
Zev Weissc4005bd2021-09-01 22:30:23 -05002762 hostDbusName += node;
2763 chassisDbusName += node;
2764 osDbusName += node;
2765 buttonDbusName += node;
2766 nmiDbusName += node;
2767 rstCauseDbusName += node;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002768
Priyatharshan P70120512020-09-16 18:47:20 +05302769 // Request all the dbus names
2770 conn->request_name(hostDbusName.c_str());
2771 conn->request_name(chassisDbusName.c_str());
2772 conn->request_name(osDbusName.c_str());
2773 conn->request_name(buttonDbusName.c_str());
2774 conn->request_name(nmiDbusName.c_str());
2775 conn->request_name(rstCauseDbusName.c_str());
2776
2777 if (sioPwrGoodConfig.lineName.empty() ||
2778 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
Priyatharshan P19c47a32020-08-12 18:16:43 +05302779 {
Lei YU92caa4c2021-02-23 16:59:25 +08002780 sioEnabled = false;
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002781 lg2::info("SIO control GPIOs not defined, disable SIO support.");
Priyatharshan P19c47a32020-08-12 18:16:43 +05302782 }
2783
Jason M. Bills35471132025-05-06 12:26:10 -07002784 // Request power OK GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302785 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002786 {
Jason M. Bills35471132025-05-06 12:26:10 -07002787 if (!requestGPIOEvents(powerOkConfig.lineName, powerOKHandler,
2788 powerOKLine, powerOKEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302789 {
2790 return -1;
2791 }
2792 }
Priyatharshan P70120512020-09-16 18:47:20 +05302793 else if (powerOkConfig.type == ConfigType::DBUS)
2794 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002795 static sdbusplus::bus::match_t powerOkEventMonitor =
Jason M. Bills35471132025-05-06 12:26:10 -07002796 power_control::dbusGPIOMatcher(powerOkConfig, powerOKHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302797 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302798 else
2799 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002800 lg2::error("PowerOk name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002801 return -1;
2802 }
2803
Lei YU92caa4c2021-02-23 16:59:25 +08002804 if (sioEnabled == true)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002805 {
Priyatharshan P19c47a32020-08-12 18:16:43 +05302806 // Request SIO_POWER_GOOD GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302807 if (sioPwrGoodConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302808 {
Priyatharshan P70120512020-09-16 18:47:20 +05302809 if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002810 sioPowerGoodHandler, sioPowerGoodLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302811 sioPowerGoodEvent))
2812 {
2813 return -1;
2814 }
2815 }
2816 else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2817 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002818 static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002819 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2820 sioPowerGoodHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302821 }
2822 else
2823 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002824 lg2::error(
Priyatharshan P70120512020-09-16 18:47:20 +05302825 "sioPwrGood name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302826 return -1;
2827 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002828
Priyatharshan P19c47a32020-08-12 18:16:43 +05302829 // Request SIO_ONCONTROL GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302830 if (sioOnControlConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302831 {
Priyatharshan P70120512020-09-16 18:47:20 +05302832 if (!requestGPIOEvents(sioOnControlConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002833 sioOnControlHandler, sioOnControlLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302834 sioOnControlEvent))
2835 {
2836 return -1;
2837 }
2838 }
2839 else if (sioOnControlConfig.type == ConfigType::DBUS)
2840 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002841 static sdbusplus::bus::match_t sioOnControlEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002842 power_control::dbusGPIOMatcher(sioOnControlConfig,
2843 sioOnControlHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302844 }
2845 else
2846 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002847 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002848 "sioOnControl name should be configured from jsonconfig file\n");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302849 return -1;
2850 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002851
Priyatharshan P19c47a32020-08-12 18:16:43 +05302852 // Request SIO_S5 GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302853 if (sioS5Config.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302854 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002855 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
Priyatharshan P70120512020-09-16 18:47:20 +05302856 sioS5Line, sioS5Event))
2857 {
2858 return -1;
2859 }
2860 }
2861 else if (sioS5Config.type == ConfigType::DBUS)
2862 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002863 static sdbusplus::bus::match_t sioS5EventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002864 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
Priyatharshan P70120512020-09-16 18:47:20 +05302865 }
2866 else
2867 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002868 lg2::error("sioS5 name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302869 return -1;
2870 }
2871 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002872
2873 // Request POWER_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302874 if (powerButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002875 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002876 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2877 powerButtonLine, powerButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302878 {
2879 return -1;
2880 }
2881 }
Priyatharshan P70120512020-09-16 18:47:20 +05302882 else if (powerButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302883 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002884 static sdbusplus::bus::match_t powerButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002885 power_control::dbusGPIOMatcher(powerButtonConfig,
2886 powerButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002887 }
2888
2889 // Request RESET_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302890 if (resetButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002891 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002892 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2893 resetButtonLine, resetButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302894 {
2895 return -1;
2896 }
2897 }
Priyatharshan P70120512020-09-16 18:47:20 +05302898 else if (resetButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302899 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002900 static sdbusplus::bus::match_t resetButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002901 power_control::dbusGPIOMatcher(resetButtonConfig,
2902 resetButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002903 }
2904
2905 // Request NMI_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302906 if (nmiButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302907 {
Priyatharshan P70120512020-09-16 18:47:20 +05302908 if (!nmiButtonConfig.lineName.empty())
2909 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002910 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302911 nmiButtonLine, nmiButtonEvent);
2912 }
2913 }
2914 else if (nmiButtonConfig.type == ConfigType::DBUS)
2915 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002916 static sdbusplus::bus::match_t nmiButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002917 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302918 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002919
2920 // Request ID_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302921 if (idButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302922 {
Priyatharshan P70120512020-09-16 18:47:20 +05302923 if (!idButtonConfig.lineName.empty())
2924 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002925 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302926 idButtonLine, idButtonEvent);
2927 }
2928 }
2929 else if (idButtonConfig.type == ConfigType::DBUS)
2930 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002931 static sdbusplus::bus::match_t idButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002932 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302933 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002934
Jason M. Billsfb957332021-01-28 13:18:46 -08002935#ifdef USE_PLT_RST
Patrick Williams439b9c32022-07-22 19:26:53 -05002936 sdbusplus::bus::match_t pltRstMatch(
Lei YU92caa4c2021-02-23 16:59:25 +08002937 *conn,
Jason M. Billsfb957332021-01-28 13:18:46 -08002938 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2939 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
Lei YU92caa4c2021-02-23 16:59:25 +08002940 hostMiscHandler);
Jason M. Billsfb957332021-01-28 13:18:46 -08002941#endif
2942
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002943 // Request POST_COMPLETE GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302944 if (postCompleteConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002945 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002946 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2947 postCompleteLine, postCompleteEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302948 {
2949 return -1;
2950 }
2951 }
Priyatharshan P70120512020-09-16 18:47:20 +05302952 else if (postCompleteConfig.type == ConfigType::DBUS)
2953 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002954 static sdbusplus::bus::match_t postCompleteEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002955 power_control::dbusGPIOMatcher(postCompleteConfig,
2956 postCompleteHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302957 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302958 else
2959 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002960 lg2::error(
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002961 "postComplete name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002962 return -1;
2963 }
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
3508 // OS State Service
3509 sdbusplus::asio::object_server osServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003510 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003511
3512 // OS State Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003513 osIface = osServer.add_interface(
Potin Lai33737912024-02-23 09:53:47 +08003514 "/xyz/openbmc_project/state/host" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003515 "xyz.openbmc_project.State.OperatingSystem.Status");
3516
3517 // Get the initial OS state based on POST complete
Jason M. Billsc6d75652024-09-10 16:54:26 -07003518 // Asserted, OS state is "Standby" (ready to boot)
3519 // De-Asserted, OS state is "Inactive"
Tim Lee86239182021-12-23 11:46:01 +08003520 OperatingSystemStateStage osState;
Priyatharshan P70120512020-09-16 18:47:20 +05303521 if (postCompleteConfig.type == ConfigType::GPIO)
3522 {
Jason M. Billsc6d75652024-09-10 16:54:26 -07003523 osState = postCompleteLine.get_value() == postCompleteConfig.polarity
3524 ? OperatingSystemStateStage::Standby
3525 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303526 }
3527 else
3528 {
Tim Lee86239182021-12-23 11:46:01 +08003529 osState = getProperty(postCompleteConfig) > 0
Jason M. Billsc6d75652024-09-10 16:54:26 -07003530 ? OperatingSystemStateStage::Standby
3531 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303532 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003533
Tim Lee86239182021-12-23 11:46:01 +08003534 osIface->register_property(
3535 "OperatingSystemState",
3536 std::string(getOperatingSystemStateStage(osState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003537
Lei YU92caa4c2021-02-23 16:59:25 +08003538 osIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003539
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003540 // Restart Cause Service
3541 sdbusplus::asio::object_server restartCauseServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003542 sdbusplus::asio::object_server(conn);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003543
3544 // Restart Cause Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003545 restartCauseIface = restartCauseServer.add_interface(
Naveen Mosesec972d82021-07-16 21:19:23 +05303546 "/xyz/openbmc_project/control/host" + node + "/restart_cause",
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003547 "xyz.openbmc_project.Control.Host.RestartCause");
3548
Lei YU92caa4c2021-02-23 16:59:25 +08003549 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003550 "RestartCause",
3551 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3552
Lei YU92caa4c2021-02-23 16:59:25 +08003553 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003554 "RequestedRestartCause",
3555 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3556 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003557 if (requested ==
3558 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3559 {
3560 addRestartCause(RestartCause::watchdog);
3561 }
3562 else
3563 {
3564 throw std::invalid_argument(
3565 "Unrecognized RestartCause Request");
3566 return 0;
3567 }
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003568
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003569 lg2::info("RestartCause requested: {RESTART_CAUSE}",
3570 "RESTART_CAUSE", requested);
3571 resp = requested;
3572 return 1;
3573 });
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003574
Lei YU92caa4c2021-02-23 16:59:25 +08003575 restartCauseIface->initialize();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003576
Lei YU92caa4c2021-02-23 16:59:25 +08003577 currentHostStateMonitor();
Yong Li8d660212019-12-27 10:18:10 +08003578
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +03003579 if (!hpmStbyEnConfig.lineName.empty())
3580 {
3581 // Set to indicate BMC's power control module is ready to take
3582 // the inputs [PWR_GOOD] from the HPM FPGA
3583 gpiod::line hpmLine;
3584 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3585 hpmLine))
3586 {
3587 return -1;
3588 }
3589 }
3590
Lei YU92caa4c2021-02-23 16:59:25 +08003591 io.run();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003592
3593 return 0;
3594}