blob: 0fd422da3d1cdd0d71d9980acb707ccc3dbfe52c [file] [log] [blame]
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001/*
2// Copyright (c) 2018-2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +030016#include "power_control.hpp"
17
Ed Tanousf61ca6f2019-08-15 15:09:05 -070018#include <sys/sysinfo.h>
19#include <systemd/sd-journal.h>
20
Ed Tanous744e9a92023-02-28 13:35:22 -080021#include <boost/asio/io_context.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070022#include <boost/asio/posix/stream_descriptor.hpp>
Jason M. Billse63dea02020-08-27 12:07:35 -070023#include <boost/asio/steady_timer.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070024#include <boost/container/flat_map.hpp>
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070025#include <boost/container/flat_set.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070026#include <gpiod.hpp>
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053027#include <nlohmann/json.hpp>
Jason M. Billsc46ebb42021-11-10 11:41:31 -080028#include <phosphor-logging/lg2.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070029#include <sdbusplus/asio/object_server.hpp>
Vijay Khemka2b6f4422020-05-29 11:13:23 -070030
31#include <filesystem>
32#include <fstream>
Zev Weissca478552024-06-11 23:45:58 +000033#include <optional>
34#include <regex>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070035#include <string_view>
36
37namespace power_control
38{
Ed Tanous744e9a92023-02-28 13:35:22 -080039static boost::asio::io_context io;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070040std::shared_ptr<sdbusplus::asio::connection> conn;
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +030041PersistentState appState;
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +030042PowerRestoreController powerRestore(io);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053043
44static std::string node = "0";
Andrei Kartashev3efcf372021-12-29 15:32:17 +030045static const std::string appName = "power-control";
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053046
Priyatharshan P70120512020-09-16 18:47:20 +053047enum class DbusConfigType
48{
49 name = 1,
50 path,
51 interface,
52 property
53};
Zev Weissca478552024-06-11 23:45:58 +000054
55// Mandatory config parameters for dbus inputs
Priyatharshan P70120512020-09-16 18:47:20 +053056boost::container::flat_map<DbusConfigType, std::string> dbusParams = {
57 {DbusConfigType::name, "DbusName"},
58 {DbusConfigType::path, "Path"},
59 {DbusConfigType::interface, "Interface"},
60 {DbusConfigType::property, "Property"}};
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053061
Priyatharshan P70120512020-09-16 18:47:20 +053062enum class ConfigType
63{
64 GPIO = 1,
65 DBUS
66};
67
68struct ConfigData
69{
70 std::string name;
71 std::string lineName;
72 std::string dbusName;
73 std::string path;
74 std::string interface;
Zev Weissca478552024-06-11 23:45:58 +000075 std::optional<std::regex> matchRegex;
Jean-Marie Verdun50937e72021-08-31 09:15:49 -070076 bool polarity;
Priyatharshan P70120512020-09-16 18:47:20 +053077 ConfigType type;
78};
79
80static ConfigData powerOutConfig;
81static ConfigData powerOkConfig;
82static ConfigData resetOutConfig;
83static ConfigData nmiOutConfig;
84static ConfigData sioPwrGoodConfig;
85static ConfigData sioOnControlConfig;
86static ConfigData sioS5Config;
87static ConfigData postCompleteConfig;
88static ConfigData powerButtonConfig;
89static ConfigData resetButtonConfig;
90static ConfigData idButtonConfig;
91static ConfigData nmiButtonConfig;
Naveen Moses117c34e2021-05-26 20:10:51 +053092static ConfigData slotPowerConfig;
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +030093static ConfigData hpmStbyEnConfig;
Naveen Moses117c34e2021-05-26 20:10:51 +053094
Priyatharshan P70120512020-09-16 18:47:20 +053095// map for storing list of gpio parameters whose config are to be read from x86
96// power control json config
97boost::container::flat_map<std::string, ConfigData*> powerSignalMap = {
98 {"PowerOut", &powerOutConfig},
99 {"PowerOk", &powerOkConfig},
100 {"ResetOut", &resetOutConfig},
101 {"NMIOut", &nmiOutConfig},
102 {"SioPowerGood", &sioPwrGoodConfig},
103 {"SioOnControl", &sioOnControlConfig},
104 {"SIOS5", &sioS5Config},
105 {"PostComplete", &postCompleteConfig},
106 {"PowerButton", &powerButtonConfig},
107 {"ResetButton", &resetButtonConfig},
108 {"IdButton", &idButtonConfig},
Naveen Moses117c34e2021-05-26 20:10:51 +0530109 {"NMIButton", &nmiButtonConfig},
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +0300110 {"SlotPower", &slotPowerConfig},
111 {"HpmStbyEn", &hpmStbyEnConfig}};
Priyatharshan P70120512020-09-16 18:47:20 +0530112
113static std::string hostDbusName = "xyz.openbmc_project.State.Host";
114static std::string chassisDbusName = "xyz.openbmc_project.State.Chassis";
115static std::string osDbusName = "xyz.openbmc_project.State.OperatingSystem";
116static std::string buttonDbusName = "xyz.openbmc_project.Chassis.Buttons";
117static std::string nmiDbusName = "xyz.openbmc_project.Control.Host.NMI";
118static std::string rstCauseDbusName =
119 "xyz.openbmc_project.Control.Host.RestartCause";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700120static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
121static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
Vijay Khemka04175c22020-10-09 14:28:11 -0700122#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -0700123static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSysIface;
Naveen Moses117c34e2021-05-26 20:10:51 +0530124static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSlotIface;
Vijay Khemka04175c22020-10-09 14:28:11 -0700125#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700126static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
127static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
128static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
129static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
130static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
Chen Yugang174ec662019-08-19 19:58:49 +0800131static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700132static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700133
134static gpiod::line powerButtonMask;
135static gpiod::line resetButtonMask;
136static bool nmiButtonMasked = false;
Matt Simmering58e379d2022-09-23 14:45:50 -0700137#if IGNORE_SOFT_RESETS_DURING_POST
138static bool ignoreNextSoftReset = false;
139#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700140
Priyatharshan P70120512020-09-16 18:47:20 +0530141// This map contains all timer values that are to be read from json config
142boost::container::flat_map<std::string, int> TimerMap = {
Jason M. Billsaeefe042021-09-08 14:56:11 -0700143 {"PowerPulseMs", 200},
144 {"ForceOffPulseMs", 15000},
145 {"ResetPulseMs", 500},
146 {"PowerCycleMs", 5000},
147 {"SioPowerGoodWatchdogMs", 1000},
148 {"PsPowerOKWatchdogMs", 8000},
149 {"GracefulPowerOffS", (5 * 60)},
150 {"WarmResetCheckMs", 500},
151 {"PowerOffSaveMs", 7000},
Michal Orzeledd211e2022-10-28 13:10:16 +0200152 {"SlotPowerCycleMs", 200},
153 {"DbusGetPropertyRetry", 1000}};
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700154
155static bool nmiEnabled = true;
Olivier FAURAXd7ea2832023-04-14 14:08:02 +0000156static bool nmiWhenPoweredOff = true;
Priyatharshan P19c47a32020-08-12 18:16:43 +0530157static bool sioEnabled = true;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700158
159// Timers
160// Time holding GPIOs asserted
161static boost::asio::steady_timer gpioAssertTimer(io);
162// Time between off and on during a power cycle
163static boost::asio::steady_timer powerCycleTimer(io);
164// Time OS gracefully powering off
165static boost::asio::steady_timer gracefulPowerOffTimer(io);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700166// Time the warm reset check
167static boost::asio::steady_timer warmResetCheckTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700168// Time power supply power OK assertion on power-on
169static boost::asio::steady_timer psPowerOKWatchdogTimer(io);
170// Time SIO power good assertion on power-on
171static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
172// Time power-off state save for power loss tracking
173static boost::asio::steady_timer powerStateSaveTimer(io);
174// POH timer
175static boost::asio::steady_timer pohCounterTimer(io);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700176// Time when to allow restart cause updates
177static boost::asio::steady_timer restartCauseTimer(io);
Naveen Moses117c34e2021-05-26 20:10:51 +0530178static boost::asio::steady_timer slotPowerCycleTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700179
Michal Orzeledd211e2022-10-28 13:10:16 +0200180// Map containing timers used for D-Bus get-property retries
181static boost::container::flat_map<std::string, boost::asio::steady_timer>
182 dBusRetryTimers;
183
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700184// GPIO Lines and Event Descriptors
185static gpiod::line psPowerOKLine;
186static boost::asio::posix::stream_descriptor psPowerOKEvent(io);
187static gpiod::line sioPowerGoodLine;
188static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
189static gpiod::line sioOnControlLine;
190static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
191static gpiod::line sioS5Line;
192static boost::asio::posix::stream_descriptor sioS5Event(io);
193static gpiod::line powerButtonLine;
194static boost::asio::posix::stream_descriptor powerButtonEvent(io);
195static gpiod::line resetButtonLine;
196static boost::asio::posix::stream_descriptor resetButtonEvent(io);
197static gpiod::line nmiButtonLine;
198static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
199static gpiod::line idButtonLine;
200static boost::asio::posix::stream_descriptor idButtonEvent(io);
201static gpiod::line postCompleteLine;
202static boost::asio::posix::stream_descriptor postCompleteEvent(io);
203static gpiod::line nmiOutLine;
Naveen Moses117c34e2021-05-26 20:10:51 +0530204static gpiod::line slotPowerLine;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700205
206static constexpr uint8_t beepPowerFail = 8;
207
208static void beep(const uint8_t& beepPriority)
209{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800210 lg2::info("Beep with priority: {BEEP_PRIORITY}", "BEEP_PRIORITY",
211 beepPriority);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700212
213 conn->async_method_call(
214 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400215 if (ec)
216 {
217 lg2::error(
218 "beep returned error with async_method_call (ec = {ERROR_MSG})",
219 "ERROR_MSG", ec.message());
220 return;
221 }
222 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700223 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
224 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
225}
226
Tim Lee86239182021-12-23 11:46:01 +0800227enum class OperatingSystemStateStage
228{
229 Inactive,
230 Standby,
231};
Matt Simmering58e379d2022-09-23 14:45:50 -0700232static OperatingSystemStateStage operatingSystemState;
Tim Lee86239182021-12-23 11:46:01 +0800233static constexpr std::string_view
234 getOperatingSystemStateStage(const OperatingSystemStateStage stage)
235{
236 switch (stage)
237 {
238 case OperatingSystemStateStage::Inactive:
239 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
240 break;
241 case OperatingSystemStateStage::Standby:
242 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Standby";
243 break;
244 default:
245 return "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive";
246 break;
247 }
248};
249static void setOperatingSystemState(const OperatingSystemStateStage stage)
250{
Matt Simmering58e379d2022-09-23 14:45:50 -0700251 operatingSystemState = stage;
252#if IGNORE_SOFT_RESETS_DURING_POST
253 // If POST complete has asserted set ignoreNextSoftReset to false to avoid
254 // masking soft resets after POST
255 if (operatingSystemState == OperatingSystemStateStage::Standby)
256 {
257 ignoreNextSoftReset = false;
258 }
259#endif
Tim Lee86239182021-12-23 11:46:01 +0800260 osIface->set_property("OperatingSystemState",
261 std::string(getOperatingSystemStateStage(stage)));
262
263 lg2::info("Moving os state to {STATE} stage", "STATE",
264 getOperatingSystemStateStage(stage));
265}
266
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700267enum class PowerState
268{
269 on,
270 waitForPSPowerOK,
271 waitForSIOPowerGood,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700272 off,
273 transitionToOff,
274 gracefulTransitionToOff,
275 cycleOff,
276 transitionToCycleOff,
277 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700278 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700279};
280static PowerState powerState;
281static std::string getPowerStateName(PowerState state)
282{
283 switch (state)
284 {
285 case PowerState::on:
286 return "On";
287 break;
288 case PowerState::waitForPSPowerOK:
289 return "Wait for Power Supply Power OK";
290 break;
291 case PowerState::waitForSIOPowerGood:
292 return "Wait for SIO Power Good";
293 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700294 case PowerState::off:
295 return "Off";
296 break;
297 case PowerState::transitionToOff:
298 return "Transition to Off";
299 break;
300 case PowerState::gracefulTransitionToOff:
301 return "Graceful Transition to Off";
302 break;
303 case PowerState::cycleOff:
304 return "Power Cycle Off";
305 break;
306 case PowerState::transitionToCycleOff:
307 return "Transition to Power Cycle Off";
308 break;
309 case PowerState::gracefulTransitionToCycleOff:
310 return "Graceful Transition to Power Cycle Off";
311 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700312 case PowerState::checkForWarmReset:
313 return "Check for Warm Reset";
314 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700315 default:
316 return "unknown state: " + std::to_string(static_cast<int>(state));
317 break;
318 }
319}
320static void logStateTransition(const PowerState state)
321{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800322 lg2::info("Host{HOST}: Moving to \"{STATE}\" state", "HOST", node, "STATE",
323 getPowerStateName(state));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700324}
325
326enum class Event
327{
328 psPowerOKAssert,
329 psPowerOKDeAssert,
330 sioPowerGoodAssert,
331 sioPowerGoodDeAssert,
332 sioS5Assert,
333 sioS5DeAssert,
Jason M. Billsfb957332021-01-28 13:18:46 -0800334 pltRstAssert,
335 pltRstDeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700336 postCompleteAssert,
337 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700338 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700339 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700340 powerCycleTimerExpired,
341 psPowerOKWatchdogTimerExpired,
342 sioPowerGoodWatchdogTimerExpired,
343 gracefulPowerOffTimerExpired,
344 powerOnRequest,
345 powerOffRequest,
346 powerCycleRequest,
347 resetRequest,
348 gracefulPowerOffRequest,
349 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700350 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700351};
352static std::string getEventName(Event event)
353{
354 switch (event)
355 {
356 case Event::psPowerOKAssert:
357 return "power supply power OK assert";
358 break;
359 case Event::psPowerOKDeAssert:
360 return "power supply power OK de-assert";
361 break;
362 case Event::sioPowerGoodAssert:
363 return "SIO power good assert";
364 break;
365 case Event::sioPowerGoodDeAssert:
366 return "SIO power good de-assert";
367 break;
368 case Event::sioS5Assert:
369 return "SIO S5 assert";
370 break;
371 case Event::sioS5DeAssert:
372 return "SIO S5 de-assert";
373 break;
Jason M. Billsfb957332021-01-28 13:18:46 -0800374 case Event::pltRstAssert:
375 return "PLT_RST assert";
376 break;
377 case Event::pltRstDeAssert:
378 return "PLT_RST de-assert";
379 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700380 case Event::postCompleteAssert:
381 return "POST Complete assert";
382 break;
383 case Event::postCompleteDeAssert:
384 return "POST Complete de-assert";
385 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700386 case Event::powerButtonPressed:
387 return "power button pressed";
388 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700389 case Event::resetButtonPressed:
390 return "reset button pressed";
391 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700392 case Event::powerCycleTimerExpired:
393 return "power cycle timer expired";
394 break;
395 case Event::psPowerOKWatchdogTimerExpired:
396 return "power supply power OK watchdog timer expired";
397 break;
398 case Event::sioPowerGoodWatchdogTimerExpired:
399 return "SIO power good watchdog timer expired";
400 break;
401 case Event::gracefulPowerOffTimerExpired:
402 return "graceful power-off timer expired";
403 break;
404 case Event::powerOnRequest:
405 return "power-on request";
406 break;
407 case Event::powerOffRequest:
408 return "power-off request";
409 break;
410 case Event::powerCycleRequest:
411 return "power-cycle request";
412 break;
413 case Event::resetRequest:
414 return "reset request";
415 break;
416 case Event::gracefulPowerOffRequest:
417 return "graceful power-off request";
418 break;
419 case Event::gracefulPowerCycleRequest:
420 return "graceful power-cycle request";
421 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700422 case Event::warmResetDetected:
423 return "warm reset detected";
424 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700425 default:
426 return "unknown event: " + std::to_string(static_cast<int>(event));
427 break;
428 }
429}
430static void logEvent(const std::string_view stateHandler, const Event event)
431{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800432 lg2::info("{STATE_HANDLER}: {EVENT} event received", "STATE_HANDLER",
433 stateHandler, "EVENT", getEventName(event));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700434}
435
436// Power state handlers
437static void powerStateOn(const Event event);
438static void powerStateWaitForPSPowerOK(const Event event);
439static void powerStateWaitForSIOPowerGood(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700440static void powerStateOff(const Event event);
441static void powerStateTransitionToOff(const Event event);
442static void powerStateGracefulTransitionToOff(const Event event);
443static void powerStateCycleOff(const Event event);
444static void powerStateTransitionToCycleOff(const Event event);
445static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700446static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700447
448static std::function<void(const Event)> getPowerStateHandler(PowerState state)
449{
450 switch (state)
451 {
452 case PowerState::on:
453 return powerStateOn;
454 break;
455 case PowerState::waitForPSPowerOK:
456 return powerStateWaitForPSPowerOK;
457 break;
458 case PowerState::waitForSIOPowerGood:
459 return powerStateWaitForSIOPowerGood;
460 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700461 case PowerState::off:
462 return powerStateOff;
463 break;
464 case PowerState::transitionToOff:
465 return powerStateTransitionToOff;
466 break;
467 case PowerState::gracefulTransitionToOff:
468 return powerStateGracefulTransitionToOff;
469 break;
470 case PowerState::cycleOff:
471 return powerStateCycleOff;
472 break;
473 case PowerState::transitionToCycleOff:
474 return powerStateTransitionToCycleOff;
475 break;
476 case PowerState::gracefulTransitionToCycleOff:
477 return powerStateGracefulTransitionToCycleOff;
478 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700479 case PowerState::checkForWarmReset:
480 return powerStateCheckForWarmReset;
481 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700482 default:
Zev Weiss047bcb52020-08-20 21:28:11 +0000483 return nullptr;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700484 break;
485 }
486};
487
488static void sendPowerControlEvent(const Event event)
489{
490 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
491 if (handler == nullptr)
492 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800493 lg2::error("Failed to find handler for power state: {STATE}", "STATE",
494 static_cast<int>(powerState));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700495 return;
496 }
497 handler(event);
498}
499
500static uint64_t getCurrentTimeMs()
501{
502 struct timespec time = {};
503
504 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
505 {
506 return 0;
507 }
508 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
509 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
510
511 return currentTimeMs;
512}
513
514static constexpr std::string_view getHostState(const PowerState state)
515{
516 switch (state)
517 {
518 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700519 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700520 case PowerState::gracefulTransitionToCycleOff:
521 return "xyz.openbmc_project.State.Host.HostState.Running";
522 break;
523 case PowerState::waitForPSPowerOK:
524 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700525 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700526 case PowerState::transitionToOff:
527 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700528 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700529 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700530 return "xyz.openbmc_project.State.Host.HostState.Off";
531 break;
532 default:
533 return "";
534 break;
535 }
536};
537static constexpr std::string_view getChassisState(const PowerState state)
538{
539 switch (state)
540 {
541 case PowerState::on:
542 case PowerState::transitionToOff:
543 case PowerState::gracefulTransitionToOff:
544 case PowerState::transitionToCycleOff:
545 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700546 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700547 return "xyz.openbmc_project.State.Chassis.PowerState.On";
548 break;
549 case PowerState::waitForPSPowerOK:
550 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700551 case PowerState::off:
552 case PowerState::cycleOff:
553 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
554 break;
555 default:
556 return "";
557 break;
558 }
559};
Naveen Moses117c34e2021-05-26 20:10:51 +0530560#ifdef CHASSIS_SYSTEM_RESET
561enum class SlotPowerState
562{
563 on,
564 off,
565};
566static SlotPowerState slotPowerState;
567static constexpr std::string_view getSlotState(const SlotPowerState state)
568{
569 switch (state)
570 {
571 case SlotPowerState::on:
572 return "xyz.openbmc_project.State.Chassis.PowerState.On";
573 break;
574 case SlotPowerState::off:
575 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
576 break;
577 default:
578 return "";
579 break;
580 }
581};
582static void setSlotPowerState(const SlotPowerState state)
583{
584 slotPowerState = state;
585 chassisSlotIface->set_property("CurrentPowerState",
586 std::string(getSlotState(slotPowerState)));
587 chassisSlotIface->set_property("LastStateChangeTime", getCurrentTimeMs());
588}
589#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700590static void savePowerState(const PowerState state)
591{
592 powerStateSaveTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -0700593 std::chrono::milliseconds(TimerMap["PowerOffSaveMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700594 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
595 if (ec)
596 {
597 // operation_aborted is expected if timer is canceled before
598 // completion.
599 if (ec != boost::asio::error::operation_aborted)
600 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800601 lg2::error("Power-state save async_wait failed: {ERROR_MSG}",
602 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700603 }
604 return;
605 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300606 appState.set(PersistentState::Params::PowerState,
607 std::string{getChassisState(state)});
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700608 });
609}
610static void setPowerState(const PowerState state)
611{
612 powerState = state;
613 logStateTransition(state);
614
615 hostIface->set_property("CurrentHostState",
616 std::string(getHostState(powerState)));
617
618 chassisIface->set_property("CurrentPowerState",
619 std::string(getChassisState(powerState)));
620 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
621
622 // Save the power state for the restore policy
623 savePowerState(state);
624}
625
626enum class RestartCause
627{
628 command,
629 resetButton,
630 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700631 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700632 powerPolicyOn,
633 powerPolicyRestore,
634 softReset,
635};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700636static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700637static std::string getRestartCause(RestartCause cause)
638{
639 switch (cause)
640 {
641 case RestartCause::command:
642 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
643 break;
644 case RestartCause::resetButton:
645 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
646 break;
647 case RestartCause::powerButton:
648 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
649 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700650 case RestartCause::watchdog:
651 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
652 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700653 case RestartCause::powerPolicyOn:
Jason M. Bills418ce112021-09-08 15:15:05 -0700654 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700655 break;
656 case RestartCause::powerPolicyRestore:
Jason M. Bills418ce112021-09-08 15:15:05 -0700657 return "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyPreviousState";
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700658 break;
659 case RestartCause::softReset:
660 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
661 break;
662 default:
663 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
664 break;
665 }
666}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700667static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700668{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700669 // Add this to the set of causes for this restart
670 causeSet.insert(cause);
671}
672static void clearRestartCause()
673{
674 // Clear the set for the next restart
675 causeSet.clear();
676}
677static void setRestartCauseProperty(const std::string& cause)
678{
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800679 lg2::info("RestartCause set to {RESTART_CAUSE}", "RESTART_CAUSE", cause);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700680 restartCauseIface->set_property("RestartCause", cause);
681}
Rashmi RV89f61312020-01-22 15:41:50 +0530682
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300683#ifdef USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530684static void resetACBootProperty()
685{
686 if ((causeSet.contains(RestartCause::command)) ||
687 (causeSet.contains(RestartCause::softReset)))
688 {
689 conn->async_method_call(
690 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400691 if (ec)
692 {
693 lg2::error("failed to reset ACBoot property");
694 }
695 },
Rashmi RV89f61312020-01-22 15:41:50 +0530696 "xyz.openbmc_project.Settings",
697 "/xyz/openbmc_project/control/host0/ac_boot",
698 "org.freedesktop.DBus.Properties", "Set",
699 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
700 std::variant<std::string>{"False"});
701 }
702}
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300703#endif // USE_ACBOOT
Rashmi RV89f61312020-01-22 15:41:50 +0530704
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700705static void setRestartCause()
706{
707 // Determine the actual restart cause based on the set of causes
708 std::string restartCause =
709 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
710 if (causeSet.contains(RestartCause::watchdog))
711 {
712 restartCause = getRestartCause(RestartCause::watchdog);
713 }
714 else if (causeSet.contains(RestartCause::command))
715 {
716 restartCause = getRestartCause(RestartCause::command);
717 }
718 else if (causeSet.contains(RestartCause::resetButton))
719 {
720 restartCause = getRestartCause(RestartCause::resetButton);
721 }
722 else if (causeSet.contains(RestartCause::powerButton))
723 {
724 restartCause = getRestartCause(RestartCause::powerButton);
725 }
726 else if (causeSet.contains(RestartCause::powerPolicyOn))
727 {
728 restartCause = getRestartCause(RestartCause::powerPolicyOn);
729 }
730 else if (causeSet.contains(RestartCause::powerPolicyRestore))
731 {
732 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
733 }
734 else if (causeSet.contains(RestartCause::softReset))
735 {
Matt Simmering58e379d2022-09-23 14:45:50 -0700736#if IGNORE_SOFT_RESETS_DURING_POST
737 if (ignoreNextSoftReset)
738 {
739 ignoreNextSoftReset = false;
740 return;
741 }
742#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700743 restartCause = getRestartCause(RestartCause::softReset);
744 }
745
746 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700747}
748
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700749static void systemPowerGoodFailedLog()
750{
751 sd_journal_send(
752 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
753 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
754 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
Jason M. Billsaeefe042021-09-08 14:56:11 -0700755 TimerMap["SioPowerGoodWatchdogMs"], NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700756}
757
758static void psPowerOKFailedLog()
759{
760 sd_journal_send(
761 "MESSAGE=PowerControl: power supply power good failed to assert",
762 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
763 "OpenBMC.0.1.PowerSupplyPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
Jason M. Billsaeefe042021-09-08 14:56:11 -0700764 TimerMap["PsPowerOKWatchdogMs"], NULL);
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700765}
766
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700767static void powerRestorePolicyLog()
768{
769 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
770 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
771 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
772}
773
774static void powerButtonPressLog()
775{
776 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
777 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
778 "OpenBMC.0.1.PowerButtonPressed", NULL);
779}
780
781static void resetButtonPressLog()
782{
783 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
784 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
785 "OpenBMC.0.1.ResetButtonPressed", NULL);
786}
787
788static void nmiButtonPressLog()
789{
790 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
791 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
792 "OpenBMC.0.1.NMIButtonPressed", NULL);
793}
794
795static void nmiDiagIntLog()
796{
797 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
798 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
799 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
800}
801
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300802PersistentState::PersistentState()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700803{
804 // create the power control directory if it doesn't exist
805 std::error_code ec;
806 if (!(std::filesystem::create_directories(powerControlDir, ec)))
807 {
808 if (ec.value() != 0)
809 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -0800810 lg2::error("failed to create {DIR_NAME}: {ERROR_MSG}", "DIR_NAME",
811 powerControlDir.string(), "ERROR_MSG", ec.message());
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300812 throw std::runtime_error("Failed to create state directory");
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700813 }
814 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300815
816 // read saved state, it's ok, if the file doesn't exists
817 std::ifstream appStateStream(powerControlDir / stateFile);
818 if (!appStateStream.is_open())
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700819 {
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300820 lg2::info("Cannot open state file \'{PATH}\'", "PATH",
821 std::string(powerControlDir / stateFile));
822 stateData = nlohmann::json({});
823 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700824 }
Andrei Kartashev50fe8ee2022-01-04 21:24:22 +0300825 try
826 {
827 appStateStream >> stateData;
828 if (stateData.is_discarded())
829 {
830 lg2::info("Cannot parse state file \'{PATH}\'", "PATH",
831 std::string(powerControlDir / stateFile));
832 stateData = nlohmann::json({});
833 return;
834 }
835 }
836 catch (const std::exception& ex)
837 {
838 lg2::info("Cannot read state file \'{PATH}\'", "PATH",
839 std::string(powerControlDir / stateFile));
840 stateData = nlohmann::json({});
841 return;
842 }
843}
844PersistentState::~PersistentState()
845{
846 saveState();
847}
848const std::string PersistentState::get(Params parameter)
849{
850 auto val = stateData.find(getName(parameter));
851 if (val != stateData.end())
852 {
853 return val->get<std::string>();
854 }
855 return getDefault(parameter);
856}
857void PersistentState::set(Params parameter, const std::string& value)
858{
859 stateData[getName(parameter)] = value;
860 saveState();
861}
862
863const std::string PersistentState::getName(const Params parameter)
864{
865 switch (parameter)
866 {
867 case Params::PowerState:
868 return "PowerState";
869 }
870 return "";
871}
872const std::string PersistentState::getDefault(const Params parameter)
873{
874 switch (parameter)
875 {
876 case Params::PowerState:
877 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
878 }
879 return "";
880}
881void PersistentState::saveState()
882{
883 std::ofstream appStateStream(powerControlDir / stateFile, std::ios::trunc);
884 if (!appStateStream.is_open())
885 {
886 lg2::error("Cannot write state file \'{PATH}\'", "PATH",
887 std::string(powerControlDir / stateFile));
888 return;
889 }
890 appStateStream << stateData.dump(indentationSize);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700891}
892
Patrick Williams48aa1f02023-05-10 07:50:30 -0500893static constexpr const char* setingsService = "xyz.openbmc_project.Settings";
894static constexpr const char* powerRestorePolicyIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300895 "xyz.openbmc_project.Control.Power.RestorePolicy";
896#ifdef USE_ACBOOT
Patrick Williams48aa1f02023-05-10 07:50:30 -0500897static constexpr const char* powerACBootObject =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300898 "/xyz/openbmc_project/control/host0/ac_boot";
Patrick Williams48aa1f02023-05-10 07:50:30 -0500899static constexpr const char* powerACBootIface =
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300900 "xyz.openbmc_project.Common.ACBoot";
901#endif // USE_ACBOOT
902
903namespace match_rules = sdbusplus::bus::match::rules;
904
905static int powerRestoreConfigHandler(sd_bus_message* m, void* context,
906 sd_bus_error*)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700907{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300908 if (context == nullptr || m == nullptr)
909 {
910 throw std::runtime_error("Invalid match");
911 }
Patrick Williams439b9c32022-07-22 19:26:53 -0500912 sdbusplus::message_t message(m);
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300913 PowerRestoreController* powerRestore =
914 static_cast<PowerRestoreController*>(context);
915
916 if (std::string(message.get_member()) == "InterfacesAdded")
917 {
918 sdbusplus::message::object_path path;
919 boost::container::flat_map<std::string, dbusPropertiesList> data;
920
921 message.read(path, data);
922
923 for (auto& [iface, properties] : data)
924 {
925 if ((iface == powerRestorePolicyIface)
926#ifdef USE_ACBOOT
927 || (iface == powerACBootIface)
928#endif // USE_ACBOOT
929 )
930 {
931 powerRestore->setProperties(properties);
932 }
933 }
934 }
935 else if (std::string(message.get_member()) == "PropertiesChanged")
936 {
937 std::string interfaceName;
938 dbusPropertiesList propertiesChanged;
939
940 message.read(interfaceName, propertiesChanged);
941
942 powerRestore->setProperties(propertiesChanged);
943 }
944 return 1;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700945}
946
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300947void PowerRestoreController::run()
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700948{
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400949 std::string powerRestorePolicyObject =
950 "/xyz/openbmc_project/control/host" + node + "/power_restore_policy";
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300951 powerRestorePolicyLog();
952 // this list only needs to be created once
953 if (matches.empty())
954 {
955 matches.emplace_back(
956 *conn,
957 match_rules::interfacesAdded() +
958 match_rules::argNpath(0, powerRestorePolicyObject) +
959 match_rules::sender(setingsService),
960 powerRestoreConfigHandler, this);
961#ifdef USE_ACBOOT
962 matches.emplace_back(*conn,
963 match_rules::interfacesAdded() +
964 match_rules::argNpath(0, powerACBootObject) +
965 match_rules::sender(setingsService),
966 powerRestoreConfigHandler, this);
967 matches.emplace_back(*conn,
968 match_rules::propertiesChanged(powerACBootObject,
969 powerACBootIface) +
970 match_rules::sender(setingsService),
971 powerRestoreConfigHandler, this);
972#endif // USE_ACBOOT
973 }
974
975 // Check if it's already on DBus
976 conn->async_method_call(
977 [this](boost::system::error_code ec,
978 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400979 if (ec)
980 {
981 return;
982 }
983 setProperties(properties);
984 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300985 setingsService, powerRestorePolicyObject,
986 "org.freedesktop.DBus.Properties", "GetAll", powerRestorePolicyIface);
987
988#ifdef USE_ACBOOT
989 // Check if it's already on DBus
990 conn->async_method_call(
991 [this](boost::system::error_code ec,
992 const dbusPropertiesList properties) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -0400993 if (ec)
994 {
995 return;
996 }
997 setProperties(properties);
998 },
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +0300999 setingsService, powerACBootObject, "org.freedesktop.DBus.Properties",
1000 "GetAll", powerACBootIface);
1001#endif
1002}
1003
1004void PowerRestoreController::setProperties(const dbusPropertiesList& props)
1005{
1006 for (auto& [property, propValue] : props)
1007 {
1008 if (property == "PowerRestorePolicy")
1009 {
1010 const std::string* value = std::get_if<std::string>(&propValue);
1011 if (value == nullptr)
1012 {
1013 lg2::error("Unable to read Power Restore Policy");
1014 continue;
1015 }
1016 powerRestorePolicy = *value;
1017 }
1018 else if (property == "PowerRestoreDelay")
1019 {
1020 const uint64_t* value = std::get_if<uint64_t>(&propValue);
1021 if (value == nullptr)
1022 {
1023 lg2::error("Unable to read Power Restore Delay");
1024 continue;
1025 }
1026 powerRestoreDelay = *value / 1000000; // usec to sec
1027 }
1028#ifdef USE_ACBOOT
1029 else if (property == "ACBoot")
1030 {
1031 const std::string* value = std::get_if<std::string>(&propValue);
1032 if (value == nullptr)
1033 {
1034 lg2::error("Unable to read AC Boot status");
1035 continue;
1036 }
1037 acBoot = *value;
1038 }
1039#endif // USE_ACBOOT
1040 }
1041 invokeIfReady();
1042}
1043
1044void PowerRestoreController::invokeIfReady()
1045{
1046 if ((powerRestorePolicy.empty()) || (powerRestoreDelay < 0))
1047 {
1048 return;
1049 }
1050#ifdef USE_ACBOOT
1051 if (acBoot.empty() || acBoot == "Unknown")
1052 {
1053 return;
1054 }
1055#endif
1056
1057 matches.clear();
1058 if (!timerFired)
1059 {
1060 // Calculate the delay from now to meet the requested delay
1061 // Subtract the approximate uboot time
1062 static constexpr const int ubootSeconds = 20;
1063 int delay = powerRestoreDelay - ubootSeconds;
1064 // Subtract the time since boot
1065 struct sysinfo info = {};
1066 if (sysinfo(&info) == 0)
1067 {
1068 delay -= info.uptime;
1069 }
1070
1071 if (delay > 0)
1072 {
1073 powerRestoreTimer.expires_after(std::chrono::seconds(delay));
1074 lg2::info("Power Restore delay of {DELAY} seconds started", "DELAY",
1075 delay);
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001076 powerRestoreTimer.async_wait([this](const boost::system::error_code
1077 ec) {
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001078 if (ec)
1079 {
1080 // operation_aborted is expected if timer is canceled before
1081 // completion.
1082 if (ec == boost::asio::error::operation_aborted)
1083 {
1084 return;
1085 }
1086 lg2::error(
1087 "power restore policy async_wait failed: {ERROR_MSG}",
1088 "ERROR_MSG", ec.message());
1089 }
1090 else
1091 {
1092 lg2::info("Power Restore delay timer expired");
1093 }
1094 invoke();
1095 });
1096 timerFired = true;
1097 }
1098 else
1099 {
1100 invoke();
1101 }
1102 }
1103}
1104
1105void PowerRestoreController::invoke()
1106{
1107 // we want to run Power Restore only once
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001108 if (policyInvoked)
1109 {
1110 return;
1111 }
1112 policyInvoked = true;
1113
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001114 lg2::info("Invoking Power Restore Policy {POLICY}", "POLICY",
1115 powerRestorePolicy);
1116 if (powerRestorePolicy ==
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001117 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
1118 {
1119 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001120 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001121 }
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001122 else if (powerRestorePolicy ==
Jason M. Bills418ce112021-09-08 15:15:05 -07001123 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001124 {
1125 if (wasPowerDropped())
1126 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001127 lg2::info("Power was dropped, restoring Host On state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001128 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001129 setRestartCauseProperty(
1130 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001131 }
1132 else
1133 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001134 lg2::info("No power drop, restoring Host Off state");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001135 }
1136 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -07001137 // We're done with the previous power state for the restore policy, so store
1138 // the current state
1139 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001140}
1141
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001142bool PowerRestoreController::wasPowerDropped()
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001143{
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001144 std::string state = appState.get(PersistentState::Params::PowerState);
1145 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001146}
1147
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001148static void waitForGPIOEvent(
1149 const std::string& name, const std::function<void(bool)>& eventHandler,
1150 gpiod::line& line, boost::asio::posix::stream_descriptor& event)
Zev Weiss676ef2c2021-09-02 21:54:02 -05001151{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001152 event.async_wait(
1153 boost::asio::posix::stream_descriptor::wait_read,
1154 [&name, eventHandler, &line,
1155 &event](const boost::system::error_code ec) {
1156 if (ec)
1157 {
1158 lg2::error("{GPIO_NAME} fd handler error: {ERROR_MSG}",
1159 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1160 // TODO: throw here to force power-control to
1161 // restart?
1162 return;
1163 }
1164 gpiod::line_event line_event = line.event_read();
1165 eventHandler(line_event.event_type ==
1166 gpiod::line_event::RISING_EDGE);
1167 waitForGPIOEvent(name, eventHandler, line, event);
1168 });
Zev Weiss676ef2c2021-09-02 21:54:02 -05001169}
1170
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001171static bool requestGPIOEvents(
Zev Weiss676ef2c2021-09-02 21:54:02 -05001172 const std::string& name, const std::function<void(bool)>& handler,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001173 gpiod::line& gpioLine,
1174 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
1175{
1176 // Find the GPIO line
1177 gpioLine = gpiod::find_line(name);
1178 if (!gpioLine)
1179 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001180 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001181 return false;
1182 }
1183
1184 try
1185 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001186 gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001187 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001188 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001189 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001190 lg2::error("Failed to request events for {GPIO_NAME}: {ERROR}",
1191 "GPIO_NAME", name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001192 return false;
1193 }
1194
1195 int gpioLineFd = gpioLine.event_get_fd();
1196 if (gpioLineFd < 0)
1197 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001198 lg2::error("Failed to get {GPIO_NAME} fd", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001199 return false;
1200 }
1201
1202 gpioEventDescriptor.assign(gpioLineFd);
1203
Zev Weiss676ef2c2021-09-02 21:54:02 -05001204 waitForGPIOEvent(name, handler, gpioLine, gpioEventDescriptor);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001205 return true;
1206}
1207
1208static bool setGPIOOutput(const std::string& name, const int value,
1209 gpiod::line& gpioLine)
1210{
1211 // Find the GPIO line
1212 gpioLine = gpiod::find_line(name);
1213 if (!gpioLine)
1214 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001215 lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001216 return false;
1217 }
1218
1219 // Request GPIO output to specified value
1220 try
1221 {
Andrei Kartashev3efcf372021-12-29 15:32:17 +03001222 gpioLine.request({appName, gpiod::line_request::DIRECTION_OUTPUT, {}},
1223 value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001224 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001225 catch (const std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001226 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001227 lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
1228 name, "ERROR", e);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001229 return false;
1230 }
1231
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001232 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1233 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001234 return true;
1235}
1236
1237static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1238 const std::string& name, const int value,
1239 const int durationMs)
1240{
1241 // Set the masked GPIO line to the specified value
1242 maskedGPIOLine.set_value(value);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001243 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
1244 "GPIO_VALUE", value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001245 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001246 gpioAssertTimer.async_wait(
1247 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001248 // Set the masked GPIO line back to the opposite value
1249 maskedGPIOLine.set_value(!value);
1250 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1251 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001252 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001253 // operation_aborted is expected if timer is canceled before
1254 // completion.
1255 if (ec != boost::asio::error::operation_aborted)
1256 {
1257 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1258 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1259 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001260 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001261 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001262 return 0;
1263}
1264
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001265static int setGPIOOutputForMs(const ConfigData& config, const int value,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001266 const int durationMs)
1267{
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001268 // If the requested GPIO is masked, use the mask line to set the output
1269 if (powerButtonMask && config.lineName == powerOutConfig.lineName)
1270 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001271 return setMaskedGPIOOutputForMs(powerButtonMask, config.lineName, value,
1272 durationMs);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001273 }
1274 if (resetButtonMask && config.lineName == resetOutConfig.lineName)
1275 {
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001276 return setMaskedGPIOOutputForMs(resetButtonMask, config.lineName, value,
1277 durationMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001278 }
1279
1280 // No mask set, so request and set the GPIO normally
1281 gpiod::line gpioLine;
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001282 if (!setGPIOOutput(config.lineName, value, gpioLine))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001283 {
1284 return -1;
1285 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07001286 const std::string name = config.lineName;
Jean-Marie Verdun2c495cf2021-09-17 21:42:23 -04001287
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001288 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001289 gpioAssertTimer.async_wait(
1290 [gpioLine, value, name](const boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001291 // Set the GPIO line back to the opposite value
1292 gpioLine.set_value(!value);
1293 lg2::info("{GPIO_NAME} released", "GPIO_NAME", name);
1294 if (ec)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001295 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001296 // operation_aborted is expected if timer is canceled before
1297 // completion.
1298 if (ec != boost::asio::error::operation_aborted)
1299 {
1300 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
1301 "GPIO_NAME", name, "ERROR_MSG", ec.message());
1302 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001303 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001304 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001305 return 0;
1306}
1307
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001308static int assertGPIOForMs(const ConfigData& config, const int durationMs)
1309{
1310 return setGPIOOutputForMs(config, config.polarity, durationMs);
1311}
1312
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001313static void powerOn()
1314{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001315 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001316}
Naveen Moses117c34e2021-05-26 20:10:51 +05301317#ifdef CHASSIS_SYSTEM_RESET
1318static int slotPowerOn()
1319{
1320 if (power_control::slotPowerState != power_control::SlotPowerState::on)
1321 {
Naveen Moses117c34e2021-05-26 20:10:51 +05301322 slotPowerLine.set_value(1);
1323
1324 if (slotPowerLine.get_value() > 0)
1325 {
1326 setSlotPowerState(SlotPowerState::on);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001327 lg2::info("Slot Power is switched On\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301328 }
1329 else
1330 {
1331 return -1;
1332 }
1333 }
1334 else
1335 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001336 lg2::info("Slot Power is already in 'On' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301337 return -1;
1338 }
1339 return 0;
1340}
1341static int slotPowerOff()
1342{
1343 if (power_control::slotPowerState != power_control::SlotPowerState::off)
1344 {
1345 slotPowerLine.set_value(0);
1346
1347 if (!(slotPowerLine.get_value() > 0))
1348 {
1349 setSlotPowerState(SlotPowerState::off);
1350 setPowerState(PowerState::off);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001351 lg2::info("Slot Power is switched Off\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301352 }
1353 else
1354 {
1355 return -1;
1356 }
1357 }
1358 else
1359 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001360 lg2::info("Slot Power is already in 'Off' state\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301361 return -1;
1362 }
1363 return 0;
1364}
1365static void slotPowerCycle()
1366{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001367 lg2::info("Slot Power Cycle started\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301368 slotPowerOff();
1369 slotPowerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001370 std::chrono::milliseconds(TimerMap["SlotPowerCycleMs"]));
Naveen Moses117c34e2021-05-26 20:10:51 +05301371 slotPowerCycleTimer.async_wait([](const boost::system::error_code ec) {
1372 if (ec)
1373 {
1374 if (ec != boost::asio::error::operation_aborted)
1375 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001376 lg2::error(
1377 "Slot Power cycle timer async_wait failed: {ERROR_MSG}",
1378 "ERROR_MSG", ec.message());
Naveen Moses117c34e2021-05-26 20:10:51 +05301379 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001380 lg2::info("Slot Power cycle timer canceled\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301381 return;
1382 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001383 lg2::info("Slot Power cycle timer completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301384 slotPowerOn();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001385 lg2::info("Slot Power Cycle Completed\n");
Naveen Moses117c34e2021-05-26 20:10:51 +05301386 });
1387}
1388#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001389static void gracefulPowerOff()
1390{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001391 assertGPIOForMs(powerOutConfig, TimerMap["PowerPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001392}
1393
1394static void forcePowerOff()
1395{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001396 if (assertGPIOForMs(powerOutConfig, TimerMap["ForceOffPulseMs"]) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001397 {
1398 return;
1399 }
1400
Jason M. Billsc6961b62021-10-21 14:08:02 -07001401 // If the force off timer expires, then the power-button override failed
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001402 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1403 if (ec)
1404 {
1405 // operation_aborted is expected if timer is canceled before
1406 // completion.
1407 if (ec != boost::asio::error::operation_aborted)
1408 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001409 lg2::error("Force power off async_wait failed: {ERROR_MSG}",
1410 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001411 }
1412 return;
1413 }
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001414
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001415 lg2::error("Power-button override failed. Not sure what to do now.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001416 });
1417}
1418
1419static void reset()
1420{
Jason M. Billsc8c90c82021-09-22 14:34:04 -07001421 assertGPIOForMs(resetOutConfig, TimerMap["ResetPulseMs"]);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001422}
1423
1424static void gracefulPowerOffTimerStart()
1425{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001426 lg2::info("Graceful power-off timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001427 gracefulPowerOffTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001428 std::chrono::seconds(TimerMap["GracefulPowerOffS"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001429 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1430 if (ec)
1431 {
1432 // operation_aborted is expected if timer is canceled before
1433 // completion.
1434 if (ec != boost::asio::error::operation_aborted)
1435 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001436 lg2::error("Graceful power-off async_wait failed: {ERROR_MSG}",
1437 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001438 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001439 lg2::info("Graceful power-off timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001440 return;
1441 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001442 lg2::info("Graceful power-off timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001443 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1444 });
1445}
1446
1447static void powerCycleTimerStart()
1448{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001449 lg2::info("Power-cycle timer started");
Priyatharshan P70120512020-09-16 18:47:20 +05301450 powerCycleTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001451 std::chrono::milliseconds(TimerMap["PowerCycleMs"]));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001452 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1453 if (ec)
1454 {
1455 // operation_aborted is expected if timer is canceled before
1456 // completion.
1457 if (ec != boost::asio::error::operation_aborted)
1458 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001459 lg2::error("Power-cycle async_wait failed: {ERROR_MSG}",
1460 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001461 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001462 lg2::info("Power-cycle timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001463 return;
1464 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001465 lg2::info("Power-cycle timer completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001466 sendPowerControlEvent(Event::powerCycleTimerExpired);
1467 });
1468}
1469
1470static void psPowerOKWatchdogTimerStart()
1471{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001472 lg2::info("power supply power OK watchdog timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001473 psPowerOKWatchdogTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001474 std::chrono::milliseconds(TimerMap["PsPowerOKWatchdogMs"]));
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001475 psPowerOKWatchdogTimer.async_wait([](const boost::system::error_code ec) {
1476 if (ec)
1477 {
1478 // operation_aborted is expected if timer is canceled before
1479 // completion.
1480 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001481 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001482 lg2::error(
1483 "power supply power OK watchdog async_wait failed: {ERROR_MSG}",
1484 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001485 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001486 lg2::info("power supply power OK watchdog timer canceled");
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001487 return;
1488 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001489 lg2::info("power supply power OK watchdog timer expired");
Jason M. Bills41a49aa2021-03-03 16:03:25 -08001490 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1491 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001492}
1493
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001494static void warmResetCheckTimerStart()
1495{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001496 lg2::info("Warm reset check timer started");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001497 warmResetCheckTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001498 std::chrono::milliseconds(TimerMap["WarmResetCheckMs"]));
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001499 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1500 if (ec)
1501 {
1502 // operation_aborted is expected if timer is canceled before
1503 // completion.
1504 if (ec != boost::asio::error::operation_aborted)
1505 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001506 lg2::error("Warm reset check async_wait failed: {ERROR_MSG}",
1507 "ERROR_MSG", ec.message());
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001508 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001509 lg2::info("Warm reset check timer canceled");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001510 return;
1511 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001512 lg2::info("Warm reset check timer completed");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001513 sendPowerControlEvent(Event::warmResetDetected);
1514 });
1515}
1516
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001517static void pohCounterTimerStart()
1518{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001519 lg2::info("POH timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001520 // Set the time-out as 1 hour, to align with POH command in ipmid
1521 pohCounterTimer.expires_after(std::chrono::hours(1));
1522 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1523 if (ec)
1524 {
1525 // operation_aborted is expected if timer is canceled before
1526 // completion.
1527 if (ec != boost::asio::error::operation_aborted)
1528 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001529 lg2::error("POH timer async_wait failed: {ERROR_MSG}",
1530 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001531 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001532 lg2::info("POH timer canceled");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001533 return;
1534 }
1535
1536 if (getHostState(powerState) !=
1537 "xyz.openbmc_project.State.Host.HostState.Running")
1538 {
1539 return;
1540 }
1541
1542 conn->async_method_call(
1543 [](boost::system::error_code ec,
1544 const std::variant<uint32_t>& pohCounterProperty) {
1545 if (ec)
1546 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001547 lg2::error("error getting poh counter");
1548 return;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001549 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001550 const uint32_t* pohCounter =
1551 std::get_if<uint32_t>(&pohCounterProperty);
1552 if (pohCounter == nullptr)
1553 {
1554 lg2::error("unable to read poh counter");
1555 return;
1556 }
1557
1558 conn->async_method_call(
1559 [](boost::system::error_code ec) {
1560 if (ec)
1561 {
1562 lg2::error("failed to set poh counter");
1563 }
1564 },
1565 "xyz.openbmc_project.Settings",
1566 "/xyz/openbmc_project/state/chassis0",
1567 "org.freedesktop.DBus.Properties", "Set",
1568 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1569 std::variant<uint32_t>(*pohCounter + 1));
Patrick Williamsd394c882023-10-20 11:18:44 -05001570 },
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001571 "xyz.openbmc_project.Settings",
1572 "/xyz/openbmc_project/state/chassis0",
1573 "org.freedesktop.DBus.Properties", "Get",
1574 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1575
1576 pohCounterTimerStart();
1577 });
1578}
1579
1580static void currentHostStateMonitor()
1581{
Yong Li8d660212019-12-27 10:18:10 +08001582 if (getHostState(powerState) ==
1583 "xyz.openbmc_project.State.Host.HostState.Running")
1584 {
1585 pohCounterTimerStart();
1586 // Clear the restart cause set for the next restart
1587 clearRestartCause();
1588 }
1589 else
1590 {
1591 pohCounterTimer.cancel();
1592 // Set the restart cause set for this restart
1593 setRestartCause();
1594 }
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', "
1600 "arg0='xyz.openbmc_project.State.Host'",
1601 [](sdbusplus::message_t& message) {
1602 std::string intfName;
1603 std::map<std::string, std::variant<std::string>> properties;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001604
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001605 try
1606 {
1607 message.read(intfName, properties);
1608 }
1609 catch (const std::exception& e)
1610 {
1611 lg2::error("Unable to read host state: {ERROR}", "ERROR", e);
1612 return;
1613 }
1614 if (properties.empty())
1615 {
1616 lg2::error("ERROR: Empty PropertiesChanged signal received");
1617 return;
1618 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001619
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001620 // We only want to check for CurrentHostState
1621 if (properties.begin()->first != "CurrentHostState")
1622 {
1623 return;
1624 }
1625 std::string* currentHostState =
1626 std::get_if<std::string>(&(properties.begin()->second));
1627 if (currentHostState == nullptr)
1628 {
1629 lg2::error("{PROPERTY} property invalid", "PROPERTY",
1630 properties.begin()->first);
1631 return;
1632 }
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001633
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001634 if (*currentHostState ==
1635 "xyz.openbmc_project.State.Host.HostState.Running")
1636 {
1637 pohCounterTimerStart();
1638 // Clear the restart cause set for the next restart
1639 clearRestartCause();
1640 sd_journal_send("MESSAGE=Host system DC power is on",
1641 "PRIORITY=%i", LOG_INFO,
1642 "REDFISH_MESSAGE_ID=%s",
1643 "OpenBMC.0.1.DCPowerOn", NULL);
1644 }
1645 else
1646 {
1647 pohCounterTimer.cancel();
1648 // POST_COMPLETE GPIO event is not working in some platforms
1649 // when power state is changed to OFF. This resulted in
1650 // 'OperatingSystemState' to stay at 'Standby', even though
1651 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1652 // if HostState is trurned to OFF.
1653 setOperatingSystemState(OperatingSystemStateStage::Inactive);
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301654
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001655 // Set the restart cause set for this restart
1656 setRestartCause();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001657#ifdef USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001658 resetACBootProperty();
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03001659#endif // USE_ACBOOT
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001660 sd_journal_send("MESSAGE=Host system DC power is off",
1661 "PRIORITY=%i", LOG_INFO,
1662 "REDFISH_MESSAGE_ID=%s",
1663 "OpenBMC.0.1.DCPowerOff", NULL);
1664 }
1665 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001666}
1667
1668static void sioPowerGoodWatchdogTimerStart()
1669{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001670 lg2::info("SIO power good watchdog timer started");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001671 sioPowerGoodWatchdogTimer.expires_after(
Jason M. Billsaeefe042021-09-08 14:56:11 -07001672 std::chrono::milliseconds(TimerMap["SioPowerGoodWatchdogMs"]));
Patrick Williamsa0a39f82024-08-16 15:20:19 -04001673 sioPowerGoodWatchdogTimer.async_wait([](const boost::system::error_code
1674 ec) {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001675 if (ec)
1676 {
1677 // operation_aborted is expected if timer is canceled before
1678 // completion.
1679 if (ec != boost::asio::error::operation_aborted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001680 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001681 lg2::error(
1682 "SIO power good watchdog async_wait failed: {ERROR_MSG}",
1683 "ERROR_MSG", ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001684 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001685 lg2::info("SIO power good watchdog timer canceled");
1686 return;
1687 }
1688 lg2::info("SIO power good watchdog timer completed");
1689 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1690 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001691}
1692
1693static void powerStateOn(const Event event)
1694{
1695 logEvent(__FUNCTION__, event);
1696 switch (event)
1697 {
1698 case Event::psPowerOKDeAssert:
1699 setPowerState(PowerState::off);
1700 // DC power is unexpectedly lost, beep
1701 beep(beepPowerFail);
1702 break;
1703 case Event::sioS5Assert:
1704 setPowerState(PowerState::transitionToOff);
Matt Simmering58e379d2022-09-23 14:45:50 -07001705#if IGNORE_SOFT_RESETS_DURING_POST
1706 // Only recognize soft resets once host gets past POST COMPLETE
1707 if (operatingSystemState != OperatingSystemStateStage::Standby)
1708 {
1709 ignoreNextSoftReset = true;
1710 }
1711#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001712 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001713 break;
Jason M. Billsfb957332021-01-28 13:18:46 -08001714#if USE_PLT_RST
1715 case Event::pltRstAssert:
1716#else
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001717 case Event::postCompleteDeAssert:
Jason M. Billsfb957332021-01-28 13:18:46 -08001718#endif
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001719 setPowerState(PowerState::checkForWarmReset);
Matt Simmering58e379d2022-09-23 14:45:50 -07001720#if IGNORE_SOFT_RESETS_DURING_POST
1721 // Only recognize soft resets once host gets past POST COMPLETE
1722 if (operatingSystemState != OperatingSystemStateStage::Standby)
1723 {
1724 ignoreNextSoftReset = true;
1725 }
1726#endif
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001727 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001728 warmResetCheckTimerStart();
1729 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001730 case Event::powerButtonPressed:
1731 setPowerState(PowerState::gracefulTransitionToOff);
1732 gracefulPowerOffTimerStart();
1733 break;
1734 case Event::powerOffRequest:
1735 setPowerState(PowerState::transitionToOff);
1736 forcePowerOff();
1737 break;
1738 case Event::gracefulPowerOffRequest:
1739 setPowerState(PowerState::gracefulTransitionToOff);
1740 gracefulPowerOffTimerStart();
1741 gracefulPowerOff();
1742 break;
1743 case Event::powerCycleRequest:
1744 setPowerState(PowerState::transitionToCycleOff);
1745 forcePowerOff();
1746 break;
1747 case Event::gracefulPowerCycleRequest:
1748 setPowerState(PowerState::gracefulTransitionToCycleOff);
1749 gracefulPowerOffTimerStart();
1750 gracefulPowerOff();
1751 break;
Jayanth Othayothdc0bab92024-02-07 07:24:35 -06001752 case Event::resetButtonPressed:
1753 setPowerState(PowerState::checkForWarmReset);
1754 warmResetCheckTimerStart();
1755 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001756 case Event::resetRequest:
1757 reset();
1758 break;
1759 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001760 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001761 break;
1762 }
1763}
1764
1765static void powerStateWaitForPSPowerOK(const Event event)
1766{
1767 logEvent(__FUNCTION__, event);
1768 switch (event)
1769 {
1770 case Event::psPowerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301771 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001772 // Cancel any GPIO assertions held during the transition
1773 gpioAssertTimer.cancel();
1774 psPowerOKWatchdogTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301775 if (sioEnabled == true)
1776 {
1777 sioPowerGoodWatchdogTimerStart();
1778 setPowerState(PowerState::waitForSIOPowerGood);
1779 }
1780 else
1781 {
1782 setPowerState(PowerState::on);
1783 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001784 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301785 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001786 case Event::psPowerOKWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001787 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001788 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001789 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001790 case Event::sioPowerGoodAssert:
1791 psPowerOKWatchdogTimer.cancel();
1792 setPowerState(PowerState::on);
1793 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001794 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001795 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001796 break;
1797 }
1798}
1799
1800static void powerStateWaitForSIOPowerGood(const Event event)
1801{
1802 logEvent(__FUNCTION__, event);
1803 switch (event)
1804 {
1805 case Event::sioPowerGoodAssert:
1806 sioPowerGoodWatchdogTimer.cancel();
1807 setPowerState(PowerState::on);
1808 break;
1809 case Event::sioPowerGoodWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001810 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001811 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001812 break;
1813 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001814 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001815 break;
1816 }
1817}
1818
1819static void powerStateOff(const Event event)
1820{
1821 logEvent(__FUNCTION__, event);
1822 switch (event)
1823 {
1824 case Event::psPowerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301825 {
1826 if (sioEnabled == true)
1827 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001828 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301829 setPowerState(PowerState::waitForSIOPowerGood);
1830 }
1831 else
1832 {
1833 setPowerState(PowerState::on);
1834 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001835 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301836 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001837 case Event::sioS5DeAssert:
Jason M. Billsfe159032022-09-01 16:03:37 -07001838 psPowerOKWatchdogTimerStart();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001839 setPowerState(PowerState::waitForPSPowerOK);
1840 break;
Jason M. Bills273d7892020-06-17 14:46:57 -07001841 case Event::sioPowerGoodAssert:
1842 setPowerState(PowerState::on);
1843 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001844 case Event::powerButtonPressed:
1845 psPowerOKWatchdogTimerStart();
1846 setPowerState(PowerState::waitForPSPowerOK);
1847 break;
1848 case Event::powerOnRequest:
1849 psPowerOKWatchdogTimerStart();
1850 setPowerState(PowerState::waitForPSPowerOK);
1851 powerOn();
1852 break;
1853 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001854 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001855 break;
1856 }
1857}
1858
1859static void powerStateTransitionToOff(const Event event)
1860{
1861 logEvent(__FUNCTION__, event);
1862 switch (event)
1863 {
1864 case Event::psPowerOKDeAssert:
1865 // Cancel any GPIO assertions held during the transition
1866 gpioAssertTimer.cancel();
1867 setPowerState(PowerState::off);
1868 break;
1869 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001870 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001871 break;
1872 }
1873}
1874
1875static void powerStateGracefulTransitionToOff(const Event event)
1876{
1877 logEvent(__FUNCTION__, event);
1878 switch (event)
1879 {
1880 case Event::psPowerOKDeAssert:
1881 gracefulPowerOffTimer.cancel();
1882 setPowerState(PowerState::off);
1883 break;
1884 case Event::gracefulPowerOffTimerExpired:
1885 setPowerState(PowerState::on);
1886 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001887 case Event::powerOffRequest:
1888 gracefulPowerOffTimer.cancel();
1889 setPowerState(PowerState::transitionToOff);
1890 forcePowerOff();
1891 break;
1892 case Event::powerCycleRequest:
1893 gracefulPowerOffTimer.cancel();
1894 setPowerState(PowerState::transitionToCycleOff);
1895 forcePowerOff();
1896 break;
1897 case Event::resetRequest:
1898 gracefulPowerOffTimer.cancel();
1899 setPowerState(PowerState::on);
1900 reset();
1901 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001902 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001903 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001904 break;
1905 }
1906}
1907
1908static void powerStateCycleOff(const Event event)
1909{
1910 logEvent(__FUNCTION__, event);
1911 switch (event)
1912 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001913 case Event::psPowerOKAssert:
Priyatharshan P19c47a32020-08-12 18:16:43 +05301914 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001915 powerCycleTimer.cancel();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301916 if (sioEnabled == true)
1917 {
Jason M. Bills7e27d3d2021-09-08 14:51:09 -07001918 sioPowerGoodWatchdogTimerStart();
Priyatharshan P19c47a32020-08-12 18:16:43 +05301919 setPowerState(PowerState::waitForSIOPowerGood);
1920 }
1921 else
1922 {
1923 setPowerState(PowerState::on);
1924 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001925 break;
Priyatharshan P19c47a32020-08-12 18:16:43 +05301926 }
Jason M. Bills35aa6652020-04-30 16:24:55 -07001927 case Event::sioS5DeAssert:
1928 powerCycleTimer.cancel();
Jason M. Billsfe159032022-09-01 16:03:37 -07001929 psPowerOKWatchdogTimerStart();
Jason M. Bills35aa6652020-04-30 16:24:55 -07001930 setPowerState(PowerState::waitForPSPowerOK);
1931 break;
1932 case Event::powerButtonPressed:
1933 powerCycleTimer.cancel();
1934 psPowerOKWatchdogTimerStart();
1935 setPowerState(PowerState::waitForPSPowerOK);
1936 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001937 case Event::powerCycleTimerExpired:
1938 psPowerOKWatchdogTimerStart();
1939 setPowerState(PowerState::waitForPSPowerOK);
1940 powerOn();
1941 break;
1942 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001943 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001944 break;
1945 }
1946}
1947
1948static void powerStateTransitionToCycleOff(const Event event)
1949{
1950 logEvent(__FUNCTION__, event);
1951 switch (event)
1952 {
1953 case Event::psPowerOKDeAssert:
1954 // Cancel any GPIO assertions held during the transition
1955 gpioAssertTimer.cancel();
1956 setPowerState(PowerState::cycleOff);
1957 powerCycleTimerStart();
1958 break;
1959 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001960 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001961 break;
1962 }
1963}
1964
1965static void powerStateGracefulTransitionToCycleOff(const Event event)
1966{
1967 logEvent(__FUNCTION__, event);
1968 switch (event)
1969 {
1970 case Event::psPowerOKDeAssert:
1971 gracefulPowerOffTimer.cancel();
1972 setPowerState(PowerState::cycleOff);
1973 powerCycleTimerStart();
1974 break;
1975 case Event::gracefulPowerOffTimerExpired:
1976 setPowerState(PowerState::on);
1977 break;
Jason M. Bills22e0bec2021-03-04 12:54:04 -08001978 case Event::powerOffRequest:
1979 gracefulPowerOffTimer.cancel();
1980 setPowerState(PowerState::transitionToOff);
1981 forcePowerOff();
1982 break;
1983 case Event::powerCycleRequest:
1984 gracefulPowerOffTimer.cancel();
1985 setPowerState(PowerState::transitionToCycleOff);
1986 forcePowerOff();
1987 break;
1988 case Event::resetRequest:
1989 gracefulPowerOffTimer.cancel();
1990 setPowerState(PowerState::on);
1991 reset();
1992 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001993 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08001994 lg2::info("No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001995 break;
1996 }
1997}
1998
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001999static void powerStateCheckForWarmReset(const Event event)
2000{
2001 logEvent(__FUNCTION__, event);
2002 switch (event)
2003 {
2004 case Event::sioS5Assert:
2005 warmResetCheckTimer.cancel();
2006 setPowerState(PowerState::transitionToOff);
2007 break;
2008 case Event::warmResetDetected:
2009 setPowerState(PowerState::on);
2010 break;
P.K. Lee344dae82019-11-27 16:35:05 +08002011 case Event::psPowerOKDeAssert:
2012 warmResetCheckTimer.cancel();
2013 setPowerState(PowerState::off);
2014 // DC power is unexpectedly lost, beep
2015 beep(beepPowerFail);
2016 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002017 default:
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002018 lg2::info("No action taken.");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002019 break;
2020 }
2021}
2022
Zev Weiss584aa132021-09-02 19:21:52 -05002023static void psPowerOKHandler(bool state)
2024{
Zev Weissedc86f32024-05-07 01:44:33 +00002025 Event powerControlEvent = (state == powerOkConfig.polarity)
2026 ? Event::psPowerOKAssert
2027 : Event::psPowerOKDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002028 sendPowerControlEvent(powerControlEvent);
2029}
2030
Zev Weiss584aa132021-09-02 19:21:52 -05002031static void sioPowerGoodHandler(bool state)
2032{
Zev Weissedc86f32024-05-07 01:44:33 +00002033 Event powerControlEvent = (state == sioPwrGoodConfig.polarity)
2034 ? Event::sioPowerGoodAssert
2035 : Event::sioPowerGoodDeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002036 sendPowerControlEvent(powerControlEvent);
2037}
2038
Zev Weiss584aa132021-09-02 19:21:52 -05002039static void sioOnControlHandler(bool state)
2040{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002041 lg2::info("SIO_ONCONTROL value changed: {VALUE}", "VALUE",
2042 static_cast<int>(state));
Zev Weiss584aa132021-09-02 19:21:52 -05002043}
2044
Zev Weiss584aa132021-09-02 19:21:52 -05002045static void sioS5Handler(bool state)
2046{
Zev Weissedc86f32024-05-07 01:44:33 +00002047 Event powerControlEvent = (state == sioS5Config.polarity)
2048 ? Event::sioS5Assert
2049 : Event::sioS5DeAssert;
Zev Weiss584aa132021-09-02 19:21:52 -05002050 sendPowerControlEvent(powerControlEvent);
2051}
2052
Zev Weiss584aa132021-09-02 19:21:52 -05002053static void powerButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002054{
Zev Weissedc86f32024-05-07 01:44:33 +00002055 bool asserted = state == powerButtonConfig.polarity;
2056 powerButtonIface->set_property("ButtonPressed", asserted);
2057 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002058 {
2059 powerButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002060 if (!powerButtonMask)
2061 {
2062 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002063 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002064 }
2065 else
2066 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002067 lg2::info("power button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002068 }
2069 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002070#if USE_BUTTON_PASSTHROUGH
2071 gpiod::line gpioLine;
2072 bool outputState =
2073 asserted ? powerOutConfig.polarity : (!powerOutConfig.polarity);
2074 if (!setGPIOOutput(powerOutConfig.lineName, outputState, gpioLine))
2075 {
2076 lg2::error("{GPIO_NAME} power button passthrough failed", "GPIO_NAME",
2077 powerOutConfig.lineName);
2078 }
2079#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002080}
2081
Zev Weiss584aa132021-09-02 19:21:52 -05002082static void resetButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002083{
Zev Weissedc86f32024-05-07 01:44:33 +00002084 bool asserted = state == resetButtonConfig.polarity;
2085 resetButtonIface->set_property("ButtonPressed", asserted);
2086 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002087 {
2088 resetButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002089 if (!resetButtonMask)
2090 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002091 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002092 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002093 }
2094 else
2095 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002096 lg2::info("reset button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002097 }
2098 }
Renze Nicolai05e8ea82024-07-04 00:46:16 +02002099#if USE_BUTTON_PASSTHROUGH
2100 gpiod::line gpioLine;
2101 bool outputState =
2102 asserted ? resetOutConfig.polarity : (!resetOutConfig.polarity);
2103 if (!setGPIOOutput(resetOutConfig.lineName, outputState, gpioLine))
2104 {
2105 lg2::error("{GPIO_NAME} reset button passthrough failed", "GPIO_NAME",
2106 resetOutConfig.lineName);
2107 }
2108#endif
Zev Weiss584aa132021-09-02 19:21:52 -05002109}
2110
Vijay Khemka04175c22020-10-09 14:28:11 -07002111#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002112static constexpr auto systemdBusname = "org.freedesktop.systemd1";
2113static constexpr auto systemdPath = "/org/freedesktop/systemd1";
2114static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2115static constexpr auto systemTargetName = "chassis-system-reset.target";
2116
2117void systemReset()
2118{
2119 conn->async_method_call(
2120 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002121 if (ec)
2122 {
2123 lg2::error("Failed to call chassis system reset: {ERR}", "ERR",
2124 ec.message());
2125 }
2126 },
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002127 systemdBusname, systemdPath, systemdInterface, "StartUnit",
2128 systemTargetName, "replace");
2129}
Vijay Khemka04175c22020-10-09 14:28:11 -07002130#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002131
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002132static void nmiSetEnableProperty(bool value)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002133{
2134 conn->async_method_call(
2135 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002136 if (ec)
2137 {
2138 lg2::error("failed to set NMI source");
2139 }
2140 },
Chen Yugang303bd582019-11-01 08:45:06 +08002141 "xyz.openbmc_project.Settings",
2142 "/xyz/openbmc_project/Chassis/Control/NMISource",
2143 "org.freedesktop.DBus.Properties", "Set",
2144 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
2145 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002146}
2147
2148static void nmiReset(void)
2149{
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002150 const static constexpr int nmiOutPulseTimeMs = 200;
2151
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002152 lg2::info("NMI out action");
Jian Zhang461a1662022-09-22 11:29:01 +08002153 nmiOutLine.set_value(nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002154 lg2::info("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME",
Jian Zhang461a1662022-09-22 11:29:01 +08002155 nmiOutConfig.lineName, "GPIO_VALUE", nmiOutConfig.polarity);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002156 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
2157 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
2158 // restore the NMI_OUT GPIO line back to the opposite value
Jian Zhang461a1662022-09-22 11:29:01 +08002159 nmiOutLine.set_value(!nmiOutConfig.polarity);
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002160 lg2::info("{GPIO_NAME} released", "GPIO_NAME", nmiOutConfig.lineName);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002161 if (ec)
2162 {
2163 // operation_aborted is expected if timer is canceled before
2164 // completion.
2165 if (ec != boost::asio::error::operation_aborted)
2166 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002167 lg2::error("{GPIO_NAME} async_wait failed: {ERROR_MSG}",
2168 "GPIO_NAME", nmiOutConfig.lineName, "ERROR_MSG",
2169 ec.message());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002170 }
2171 }
2172 });
2173 // log to redfish
2174 nmiDiagIntLog();
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002175 lg2::info("NMI out action completed");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002176 // reset Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002177 nmiSetEnableProperty(false);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002178}
2179
2180static void nmiSourcePropertyMonitor(void)
2181{
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002182 lg2::info("NMI Source Property Monitor");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002183
Patrick Williams439b9c32022-07-22 19:26:53 -05002184 static std::unique_ptr<sdbusplus::bus::match_t> nmiSourceMatch =
2185 std::make_unique<sdbusplus::bus::match_t>(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002186 *conn,
2187 "type='signal',interface='org.freedesktop.DBus.Properties',"
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002188 "member='PropertiesChanged',"
2189 "arg0namespace='xyz.openbmc_project.Chassis.Control.NMISource'",
Patrick Williams439b9c32022-07-22 19:26:53 -05002190 [](sdbusplus::message_t& msg) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002191 std::string interfaceName;
2192 boost::container::flat_map<std::string,
2193 std::variant<bool, std::string>>
2194 propertiesChanged;
2195 std::string state;
2196 bool value = true;
2197 try
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002198 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002199 msg.read(interfaceName, propertiesChanged);
2200 if (propertiesChanged.begin()->first == "Enabled")
2201 {
2202 value =
2203 std::get<bool>(propertiesChanged.begin()->second);
2204 lg2::info(
2205 "NMI Enabled propertiesChanged value: {VALUE}",
2206 "VALUE", value);
2207 nmiEnabled = value;
2208 if (nmiEnabled)
2209 {
2210 nmiReset();
2211 }
2212 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002213 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002214 catch (const std::exception& e)
2215 {
2216 lg2::error("Unable to read NMI source: {ERROR}", "ERROR",
2217 e);
2218 return;
2219 }
2220 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002221}
2222
2223static void setNmiSource()
2224{
2225 conn->async_method_call(
2226 [](boost::system::error_code ec) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002227 if (ec)
2228 {
2229 lg2::error("failed to set NMI source");
2230 }
2231 },
Chen Yugang303bd582019-11-01 08:45:06 +08002232 "xyz.openbmc_project.Settings",
2233 "/xyz/openbmc_project/Chassis/Control/NMISource",
2234 "org.freedesktop.DBus.Properties", "Set",
2235 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
Jason M. Bills418ce112021-09-08 15:15:05 -07002236 std::variant<std::string>{
Tim Lee6af569f2024-03-11 17:32:42 +08002237 "xyz.openbmc_project.Chassis.Control.NMISource.BMCSourceSignal.FrontPanelButton"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002238 // set Enable Property
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002239 nmiSetEnableProperty(true);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002240}
2241
Zev Weiss584aa132021-09-02 19:21:52 -05002242static void nmiButtonHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002243{
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002244 // Don't handle event if host not running and config doesn't force it
2245 if (!nmiWhenPoweredOff &&
2246 getHostState(powerState) !=
2247 "xyz.openbmc_project.State.Host.HostState.Running")
2248 {
2249 return;
2250 }
Zev Weissedc86f32024-05-07 01:44:33 +00002251
2252 bool asserted = state == nmiButtonConfig.polarity;
2253 nmiButtonIface->set_property("ButtonPressed", asserted);
2254 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002255 {
2256 nmiButtonPressLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002257 if (nmiButtonMasked)
2258 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002259 lg2::info("NMI button press masked");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002260 }
2261 else
2262 {
2263 setNmiSource();
2264 }
2265 }
Zev Weiss584aa132021-09-02 19:21:52 -05002266}
2267
Zev Weiss584aa132021-09-02 19:21:52 -05002268static void idButtonHandler(bool state)
2269{
Zev Weissedc86f32024-05-07 01:44:33 +00002270 bool asserted = state == idButtonConfig.polarity;
2271 idButtonIface->set_property("ButtonPressed", asserted);
Zev Weiss584aa132021-09-02 19:21:52 -05002272}
2273
Jason M. Billsfb957332021-01-28 13:18:46 -08002274static void pltRstHandler(bool pltRst)
2275{
2276 if (pltRst)
2277 {
2278 sendPowerControlEvent(Event::pltRstDeAssert);
2279 }
2280 else
2281 {
2282 sendPowerControlEvent(Event::pltRstAssert);
2283 }
2284}
2285
Patrick Williams439b9c32022-07-22 19:26:53 -05002286[[maybe_unused]] static void hostMiscHandler(sdbusplus::message_t& msg)
Jason M. Billsfb957332021-01-28 13:18:46 -08002287{
2288 std::string interfaceName;
2289 boost::container::flat_map<std::string, std::variant<bool>>
2290 propertiesChanged;
Jason M. Billsfb957332021-01-28 13:18:46 -08002291 try
2292 {
2293 msg.read(interfaceName, propertiesChanged);
2294 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002295 catch (const std::exception& e)
Jason M. Billsfb957332021-01-28 13:18:46 -08002296 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002297 lg2::error("Unable to read Host Misc status: {ERROR}", "ERROR", e);
Jason M. Billsfb957332021-01-28 13:18:46 -08002298 return;
2299 }
2300 if (propertiesChanged.empty())
2301 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002302 lg2::error("ERROR: Empty Host.Misc PropertiesChanged signal received");
Jason M. Billsfb957332021-01-28 13:18:46 -08002303 return;
2304 }
2305
2306 for (auto& [property, value] : propertiesChanged)
2307 {
2308 if (property == "ESpiPlatformReset")
2309 {
2310 bool* pltRst = std::get_if<bool>(&value);
2311 if (pltRst == nullptr)
2312 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002313 lg2::error("{PROPERTY} property invalid", "PROPERTY", property);
Jason M. Billsfb957332021-01-28 13:18:46 -08002314 return;
2315 }
2316 pltRstHandler(*pltRst);
2317 }
2318 }
2319}
2320
Zev Weiss584aa132021-09-02 19:21:52 -05002321static void postCompleteHandler(bool state)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002322{
Zev Weissedc86f32024-05-07 01:44:33 +00002323 bool asserted = state == postCompleteConfig.polarity;
2324 if (asserted)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002325 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002326 sendPowerControlEvent(Event::postCompleteAssert);
Tim Lee86239182021-12-23 11:46:01 +08002327 setOperatingSystemState(OperatingSystemStateStage::Standby);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002328 }
2329 else
2330 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002331 sendPowerControlEvent(Event::postCompleteDeAssert);
Tim Lee86239182021-12-23 11:46:01 +08002332 setOperatingSystemState(OperatingSystemStateStage::Inactive);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002333 }
Zev Weiss584aa132021-09-02 19:21:52 -05002334}
2335
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302336static int loadConfigValues()
2337{
2338 const std::string configFilePath =
2339 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2340 ".json";
2341 std::ifstream configFile(configFilePath.c_str());
2342 if (!configFile.is_open())
2343 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002344 lg2::error("loadConfigValues: Cannot open config path \'{PATH}\'",
2345 "PATH", configFilePath);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302346 return -1;
2347 }
Zev Weiss1aa08b22021-09-15 17:06:20 -05002348 auto jsonData = nlohmann::json::parse(configFile, nullptr, true, true);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302349
Priyatharshan P70120512020-09-16 18:47:20 +05302350 if (jsonData.is_discarded())
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302351 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002352 lg2::error("Power config readings JSON parser failure");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302353 return -1;
2354 }
Priyatharshan P70120512020-09-16 18:47:20 +05302355 auto gpios = jsonData["gpio_configs"];
2356 auto timers = jsonData["timing_configs"];
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302357
Priyatharshan P70120512020-09-16 18:47:20 +05302358 ConfigData* tempGpioData;
2359
2360 for (nlohmann::json& gpioConfig : gpios)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302361 {
Priyatharshan P70120512020-09-16 18:47:20 +05302362 if (!gpioConfig.contains("Name"))
2363 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002364 lg2::error("The 'Name' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302365 return -1;
2366 }
2367
2368 // Iterate through the powersignal map to check if the gpio json config
2369 // entry is valid
2370 std::string gpioName = gpioConfig["Name"];
2371 auto signalMapIter = powerSignalMap.find(gpioName);
2372 if (signalMapIter == powerSignalMap.end())
2373 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002374 lg2::error(
2375 "{GPIO_NAME} is not a recognized power-control signal name",
2376 "GPIO_NAME", gpioName);
Priyatharshan P70120512020-09-16 18:47:20 +05302377 return -1;
2378 }
2379
2380 // assign the power signal name to the corresponding structure reference
2381 // from map then fillup the structure with coressponding json config
2382 // value
2383 tempGpioData = signalMapIter->second;
2384 tempGpioData->name = gpioName;
2385
2386 if (!gpioConfig.contains("Type"))
2387 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002388 lg2::error("The \'Type\' field must be defined in Json file");
Priyatharshan P70120512020-09-16 18:47:20 +05302389 return -1;
2390 }
2391
2392 std::string signalType = gpioConfig["Type"];
2393 if (signalType == "GPIO")
2394 {
2395 tempGpioData->type = ConfigType::GPIO;
2396 }
2397 else if (signalType == "DBUS")
2398 {
2399 tempGpioData->type = ConfigType::DBUS;
2400 }
2401 else
2402 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002403 lg2::error("{TYPE} is not a recognized power-control signal type",
2404 "TYPE", signalType);
Priyatharshan P70120512020-09-16 18:47:20 +05302405 return -1;
2406 }
2407
2408 if (tempGpioData->type == ConfigType::GPIO)
2409 {
2410 if (gpioConfig.contains("LineName"))
2411 {
2412 tempGpioData->lineName = gpioConfig["LineName"];
2413 }
2414 else
2415 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002416 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002417 "The \'LineName\' field must be defined for GPIO configuration");
Priyatharshan P70120512020-09-16 18:47:20 +05302418 return -1;
2419 }
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002420 if (gpioConfig.contains("Polarity"))
2421 {
2422 std::string polarity = gpioConfig["Polarity"];
2423 if (polarity == "ActiveLow")
2424 {
2425 tempGpioData->polarity = false;
2426 }
2427 else if (polarity == "ActiveHigh")
2428 {
2429 tempGpioData->polarity = true;
2430 }
2431 else
2432 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002433 lg2::error(
2434 "Polarity defined but not properly setup. Please only ActiveHigh or ActiveLow. Currently set to {POLARITY}",
2435 "POLARITY", polarity);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002436 return -1;
2437 }
2438 }
2439 else
2440 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002441 lg2::error("Polarity field not found for {GPIO_NAME}",
2442 "GPIO_NAME", tempGpioData->lineName);
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002443 return -1;
2444 }
Priyatharshan P70120512020-09-16 18:47:20 +05302445 }
2446 else
2447 {
2448 // if dbus based gpio config is defined read and update the dbus
2449 // params corresponding to the gpio config instance
2450 for (auto& [key, dbusParamName] : dbusParams)
2451 {
Logananth Sundararaja4308042021-10-20 11:52:05 +05302452 if (!gpioConfig.contains(dbusParamName))
Priyatharshan P70120512020-09-16 18:47:20 +05302453 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002454 lg2::error(
2455 "The {DBUS_NAME} field must be defined for Dbus configuration ",
2456 "DBUS_NAME", dbusParamName);
Priyatharshan P70120512020-09-16 18:47:20 +05302457 return -1;
2458 }
2459 }
Logananth Sundararaja4308042021-10-20 11:52:05 +05302460 tempGpioData->dbusName =
2461 gpioConfig[dbusParams[DbusConfigType::name]];
2462 tempGpioData->path = gpioConfig[dbusParams[DbusConfigType::path]];
Priyatharshan P70120512020-09-16 18:47:20 +05302463 tempGpioData->interface =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302464 gpioConfig[dbusParams[DbusConfigType::interface]];
Priyatharshan P70120512020-09-16 18:47:20 +05302465 tempGpioData->lineName =
Logananth Sundararaja4308042021-10-20 11:52:05 +05302466 gpioConfig[dbusParams[DbusConfigType::property]];
Zev Weissca478552024-06-11 23:45:58 +00002467
Zev Weissd603dd12024-06-19 21:50:52 +00002468 // dbus-based inputs must be active-high.
2469 tempGpioData->polarity = true;
2470
Zev Weissca478552024-06-11 23:45:58 +00002471 // MatchRegex is optional
2472 auto item = gpioConfig.find("MatchRegex");
2473 if (item != gpioConfig.end())
2474 {
2475 try
2476 {
2477 tempGpioData->matchRegex = std::regex(*item);
2478 }
2479 catch (const std::regex_error& e)
2480 {
2481 lg2::error("Invalid MatchRegex for {NAME}: {ERR}", "NAME",
2482 gpioName, "ERR", e.what());
2483 return -1;
2484 }
2485 }
Priyatharshan P70120512020-09-16 18:47:20 +05302486 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302487 }
2488
Priyatharshan P70120512020-09-16 18:47:20 +05302489 // read and store the timer values from json config to Timer Map
2490 for (auto& [key, timerValue] : TimerMap)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302491 {
Priyatharshan P70120512020-09-16 18:47:20 +05302492 if (timers.contains(key.c_str()))
2493 {
2494 timerValue = timers[key.c_str()];
2495 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302496 }
2497
Jonathan Doman891bbde2023-05-17 13:58:24 -07002498 // If "events_configs" key is not in json config, fallback to null
2499 auto events = jsonData.value("event_configs",
2500 nlohmann::json(nlohmann::json::value_t::null));
2501 if (events.is_object())
Olivier FAURAXd7ea2832023-04-14 14:08:02 +00002502 {
2503 nmiWhenPoweredOff = events.value("NMIWhenPoweredOff", true);
2504 }
2505
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302506 return 0;
2507}
Zev Weissa8f116a2021-09-01 21:08:30 -05002508
Zev Weissca478552024-06-11 23:45:58 +00002509template <typename T>
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002510static std::optional<T>
2511 getMessageValue(sdbusplus::message_t& msg, const std::string& name)
Zev Weissa8f116a2021-09-01 21:08:30 -05002512{
Zev Weissa8f116a2021-09-01 21:08:30 -05002513 std::string event;
Zev Weissca478552024-06-11 23:45:58 +00002514 std::string thresholdInterface;
2515 boost::container::flat_map<std::string, std::variant<T>> propertiesChanged;
2516
2517 msg.read(thresholdInterface, propertiesChanged);
2518 if (propertiesChanged.empty())
2519 {
2520 return std::nullopt;
2521 }
2522
2523 event = propertiesChanged.begin()->first;
2524 if (event.empty() || event != name)
2525 {
2526 return std::nullopt;
2527 }
2528
2529 return std::get<T>(propertiesChanged.begin()->second);
2530}
2531
2532static bool getDbusMsgGPIOState(sdbusplus::message_t& msg,
2533 const ConfigData& config, bool& value)
2534{
Zev Weissa8f116a2021-09-01 21:08:30 -05002535 try
2536 {
Zev Weissca478552024-06-11 23:45:58 +00002537 if (config.matchRegex.has_value())
Zev Weissa8f116a2021-09-01 21:08:30 -05002538 {
Zev Weissca478552024-06-11 23:45:58 +00002539 std::optional<std::string> s =
2540 getMessageValue<std::string>(msg, config.lineName);
2541 if (!s.has_value())
2542 {
2543 return false;
2544 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002545
Zev Weissca478552024-06-11 23:45:58 +00002546 std::smatch m;
2547 value = std::regex_match(s.value(), m, config.matchRegex.value());
2548 }
2549 else
Zev Weissa8f116a2021-09-01 21:08:30 -05002550 {
Zev Weissca478552024-06-11 23:45:58 +00002551 std::optional<bool> v = getMessageValue<bool>(msg, config.lineName);
2552 if (!v.has_value())
2553 {
2554 return false;
2555 }
2556 value = v.value();
Zev Weissa8f116a2021-09-01 21:08:30 -05002557 }
Zev Weissa8f116a2021-09-01 21:08:30 -05002558 return true;
2559 }
Patrick Williamsf3a33b42021-10-06 12:37:26 -05002560 catch (const std::exception& e)
Zev Weissa8f116a2021-09-01 21:08:30 -05002561 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002562 lg2::error(
2563 "exception while reading dbus property \'{DBUS_NAME}\': {ERROR}",
Zev Weissca478552024-06-11 23:45:58 +00002564 "DBUS_NAME", config.lineName, "ERROR", e);
Zev Weissa8f116a2021-09-01 21:08:30 -05002565 return false;
2566 }
2567}
2568
Patrick Williams439b9c32022-07-22 19:26:53 -05002569static sdbusplus::bus::match_t
Zev Weissa8f116a2021-09-01 21:08:30 -05002570 dbusGPIOMatcher(const ConfigData& cfg, std::function<void(bool)> onMatch)
2571{
Patrick Williamsa0a39f82024-08-16 15:20:19 -04002572 auto pulseEventMatcherCallback =
2573 [&cfg, onMatch](sdbusplus::message_t& msg) {
2574 bool value = false;
2575 if (!getDbusMsgGPIOState(msg, cfg, value))
2576 {
2577 return;
2578 }
2579 onMatch(value);
2580 };
Zev Weissa8f116a2021-09-01 21:08:30 -05002581
Patrick Williams439b9c32022-07-22 19:26:53 -05002582 return sdbusplus::bus::match_t(
2583 static_cast<sdbusplus::bus_t&>(*conn),
Zev Weissa8f116a2021-09-01 21:08:30 -05002584 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2585 "PropertiesChanged',arg0='" +
Zev Weiss1cc79212024-06-19 22:14:57 +00002586 cfg.interface + "',path='" + cfg.path + "',sender='" +
2587 cfg.dbusName + "'",
Zev Weissa8f116a2021-09-01 21:08:30 -05002588 std::move(pulseEventMatcherCallback));
2589}
2590
Michal Orzeledd211e2022-10-28 13:10:16 +02002591// D-Bus property read functions
2592void reschedulePropertyRead(const ConfigData& configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302593
Michal Orzeledd211e2022-10-28 13:10:16 +02002594int getProperty(const ConfigData& configData)
2595{
2596 std::variant<bool> resp;
2597
2598 try
Priyatharshan P70120512020-09-16 18:47:20 +05302599 {
Michal Orzeledd211e2022-10-28 13:10:16 +02002600 auto method = conn->new_method_call(
2601 configData.dbusName.c_str(), configData.path.c_str(),
2602 "org.freedesktop.DBus.Properties", "Get");
2603 method.append(configData.interface.c_str(),
2604 configData.lineName.c_str());
2605
2606 auto reply = conn->call(method);
2607 if (reply.is_method_error())
2608 {
2609 lg2::error(
2610 "Error reading {PROPERTY} D-Bus property on interface {INTERFACE} and path {PATH}",
2611 "PROPERTY", configData.lineName, "INTERFACE",
2612 configData.interface, "PATH", configData.path);
2613 return -1;
2614 }
2615
2616 reply.read(resp);
2617 }
2618 catch (const sdbusplus::exception_t& e)
2619 {
2620 lg2::error("Exception while reading {PROPERTY}: {WHAT}", "PROPERTY",
2621 configData.lineName, "WHAT", e.what());
2622 reschedulePropertyRead(configData);
Priyatharshan P70120512020-09-16 18:47:20 +05302623 return -1;
2624 }
Michal Orzeledd211e2022-10-28 13:10:16 +02002625
Logananth Sundararaj85e111e2021-11-11 13:13:13 +05302626 auto respValue = std::get_if<bool>(&resp);
Priyatharshan P70120512020-09-16 18:47:20 +05302627 if (!respValue)
2628 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002629 lg2::error("Error: {PROPERTY} D-Bus property is not the expected type",
2630 "PROPERTY", configData.lineName);
Priyatharshan P70120512020-09-16 18:47:20 +05302631 return -1;
2632 }
2633 return (*respValue);
2634}
Michal Orzeledd211e2022-10-28 13:10:16 +02002635
2636void setInitialValue(const ConfigData& configData, bool initialValue)
2637{
2638 if (configData.name == "PowerOk")
2639 {
2640 powerState = (initialValue ? PowerState::on : PowerState::off);
2641 hostIface->set_property("CurrentHostState",
2642 std::string(getHostState(powerState)));
2643 }
2644 else if (configData.name == "PowerButton")
2645 {
2646 powerButtonIface->set_property("ButtonPressed", !initialValue);
2647 }
2648 else if (configData.name == "ResetButton")
2649 {
2650 resetButtonIface->set_property("ButtonPressed", !initialValue);
2651 }
2652 else if (configData.name == "NMIButton")
2653 {
2654 nmiButtonIface->set_property("ButtonPressed", !initialValue);
2655 }
2656 else if (configData.name == "IdButton")
2657 {
2658 idButtonIface->set_property("ButtonPressed", !initialValue);
2659 }
2660 else if (configData.name == "PostComplete")
2661 {
2662 OperatingSystemStateStage osState =
Jason M. Billsc6d75652024-09-10 16:54:26 -07002663 (initialValue == postCompleteConfig.polarity
2664 ? OperatingSystemStateStage::Standby
2665 : OperatingSystemStateStage::Inactive);
Michal Orzeledd211e2022-10-28 13:10:16 +02002666 setOperatingSystemState(osState);
2667 }
2668 else
2669 {
2670 lg2::error("Unknown name {NAME}", "NAME", configData.name);
2671 }
2672}
2673
2674void reschedulePropertyRead(const ConfigData& configData)
2675{
2676 auto item = dBusRetryTimers.find(configData.name);
2677
2678 if (item == dBusRetryTimers.end())
2679 {
2680 auto newItem = dBusRetryTimers.insert(
2681 {configData.name, boost::asio::steady_timer(io)});
2682
2683 if (!newItem.second)
2684 {
2685 lg2::error("Failed to add new timer for {NAME}", "NAME",
2686 configData.name);
2687 return;
2688 }
2689
2690 item = newItem.first;
2691 }
2692
2693 auto& timer = item->second;
2694 timer.expires_after(
2695 std::chrono::milliseconds(TimerMap["DbusGetPropertyRetry"]));
2696 timer.async_wait([&configData](const boost::system::error_code ec) {
2697 if (ec)
2698 {
2699 lg2::error("Retry timer for {NAME} failed: {MSG}", "NAME",
2700 configData.name, "MSG", ec.message());
2701 dBusRetryTimers.erase(configData.name);
2702 return;
2703 }
2704
2705 int property = getProperty(configData);
2706
2707 if (property >= 0)
2708 {
2709 setInitialValue(configData, (property > 0));
2710 dBusRetryTimers.erase(configData.name);
2711 }
2712 });
2713}
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002714} // namespace power_control
2715
2716int main(int argc, char* argv[])
2717{
Lei YU92caa4c2021-02-23 16:59:25 +08002718 using namespace power_control;
Priyatharshan P70120512020-09-16 18:47:20 +05302719
2720 if (argc > 1)
2721 {
2722 node = argv[1];
2723 }
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002724 lg2::info("Start Chassis power control service for host : {NODE}", "NODE",
2725 node);
Priyatharshan P70120512020-09-16 18:47:20 +05302726
Lei YU92caa4c2021-02-23 16:59:25 +08002727 conn = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002728
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302729 // Load GPIO's through json config file
Lei YU92caa4c2021-02-23 16:59:25 +08002730 if (loadConfigValues() == -1)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302731 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002732 lg2::error("Host{NODE}: Error in Parsing...", "NODE", node);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302733 }
Naveen Mosesec972d82021-07-16 21:19:23 +05302734 /* Currently for single host based systems additional busname is added
2735 with "0" at the end of the name ex : xyz.openbmc_project.State.Host0.
2736 Going forward for single hosts the old bus name without zero numbering
2737 will be removed when all other applications adapted to the
2738 bus name with zero numbering (xyz.openbmc_project.State.Host0). */
2739
2740 if (node == "0")
2741 {
2742 // Request all the dbus names
2743 conn->request_name(hostDbusName.c_str());
2744 conn->request_name(chassisDbusName.c_str());
2745 conn->request_name(osDbusName.c_str());
2746 conn->request_name(buttonDbusName.c_str());
2747 conn->request_name(nmiDbusName.c_str());
2748 conn->request_name(rstCauseDbusName.c_str());
2749 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302750
Zev Weissc4005bd2021-09-01 22:30:23 -05002751 hostDbusName += node;
2752 chassisDbusName += node;
2753 osDbusName += node;
2754 buttonDbusName += node;
2755 nmiDbusName += node;
2756 rstCauseDbusName += node;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002757
Priyatharshan P70120512020-09-16 18:47:20 +05302758 // Request all the dbus names
2759 conn->request_name(hostDbusName.c_str());
2760 conn->request_name(chassisDbusName.c_str());
2761 conn->request_name(osDbusName.c_str());
2762 conn->request_name(buttonDbusName.c_str());
2763 conn->request_name(nmiDbusName.c_str());
2764 conn->request_name(rstCauseDbusName.c_str());
2765
2766 if (sioPwrGoodConfig.lineName.empty() ||
2767 sioOnControlConfig.lineName.empty() || sioS5Config.lineName.empty())
Priyatharshan P19c47a32020-08-12 18:16:43 +05302768 {
Lei YU92caa4c2021-02-23 16:59:25 +08002769 sioEnabled = false;
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002770 lg2::info("SIO control GPIOs not defined, disable SIO support.");
Priyatharshan P19c47a32020-08-12 18:16:43 +05302771 }
2772
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002773 // Request PS_PWROK GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302774 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002775 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002776 if (!requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302777 psPowerOKLine, psPowerOKEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302778 {
2779 return -1;
2780 }
2781 }
Priyatharshan P70120512020-09-16 18:47:20 +05302782 else if (powerOkConfig.type == ConfigType::DBUS)
2783 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002784 static sdbusplus::bus::match_t powerOkEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002785 power_control::dbusGPIOMatcher(powerOkConfig, psPowerOKHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302786 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302787 else
2788 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002789 lg2::error("PowerOk name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002790 return -1;
2791 }
2792
Lei YU92caa4c2021-02-23 16:59:25 +08002793 if (sioEnabled == true)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002794 {
Priyatharshan P19c47a32020-08-12 18:16:43 +05302795 // Request SIO_POWER_GOOD GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302796 if (sioPwrGoodConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302797 {
Priyatharshan P70120512020-09-16 18:47:20 +05302798 if (!requestGPIOEvents(sioPwrGoodConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002799 sioPowerGoodHandler, sioPowerGoodLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302800 sioPowerGoodEvent))
2801 {
2802 return -1;
2803 }
2804 }
2805 else if (sioPwrGoodConfig.type == ConfigType::DBUS)
2806 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002807 static sdbusplus::bus::match_t sioPwrGoodEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002808 power_control::dbusGPIOMatcher(sioPwrGoodConfig,
2809 sioPowerGoodHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302810 }
2811 else
2812 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002813 lg2::error(
Priyatharshan P70120512020-09-16 18:47:20 +05302814 "sioPwrGood name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302815 return -1;
2816 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002817
Priyatharshan P19c47a32020-08-12 18:16:43 +05302818 // Request SIO_ONCONTROL GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302819 if (sioOnControlConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302820 {
Priyatharshan P70120512020-09-16 18:47:20 +05302821 if (!requestGPIOEvents(sioOnControlConfig.lineName,
Zev Weiss676ef2c2021-09-02 21:54:02 -05002822 sioOnControlHandler, sioOnControlLine,
Priyatharshan P70120512020-09-16 18:47:20 +05302823 sioOnControlEvent))
2824 {
2825 return -1;
2826 }
2827 }
2828 else if (sioOnControlConfig.type == ConfigType::DBUS)
2829 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002830 static sdbusplus::bus::match_t sioOnControlEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002831 power_control::dbusGPIOMatcher(sioOnControlConfig,
2832 sioOnControlHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302833 }
2834 else
2835 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002836 lg2::error(
Jason M. Bills418ce112021-09-08 15:15:05 -07002837 "sioOnControl name should be configured from jsonconfig file\n");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302838 return -1;
2839 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002840
Priyatharshan P19c47a32020-08-12 18:16:43 +05302841 // Request SIO_S5 GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302842 if (sioS5Config.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302843 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002844 if (!requestGPIOEvents(sioS5Config.lineName, sioS5Handler,
Priyatharshan P70120512020-09-16 18:47:20 +05302845 sioS5Line, sioS5Event))
2846 {
2847 return -1;
2848 }
2849 }
2850 else if (sioS5Config.type == ConfigType::DBUS)
2851 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002852 static sdbusplus::bus::match_t sioS5EventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002853 power_control::dbusGPIOMatcher(sioS5Config, sioS5Handler);
Priyatharshan P70120512020-09-16 18:47:20 +05302854 }
2855 else
2856 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002857 lg2::error("sioS5 name should be configured from json config file");
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302858 return -1;
2859 }
2860 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002861
2862 // Request POWER_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302863 if (powerButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002864 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002865 if (!requestGPIOEvents(powerButtonConfig.lineName, powerButtonHandler,
2866 powerButtonLine, powerButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302867 {
2868 return -1;
2869 }
2870 }
Priyatharshan P70120512020-09-16 18:47:20 +05302871 else if (powerButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302872 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002873 static sdbusplus::bus::match_t powerButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002874 power_control::dbusGPIOMatcher(powerButtonConfig,
2875 powerButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002876 }
2877
2878 // Request RESET_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302879 if (resetButtonConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002880 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002881 if (!requestGPIOEvents(resetButtonConfig.lineName, resetButtonHandler,
2882 resetButtonLine, resetButtonEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302883 {
2884 return -1;
2885 }
2886 }
Priyatharshan P70120512020-09-16 18:47:20 +05302887 else if (resetButtonConfig.type == ConfigType::DBUS)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302888 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002889 static sdbusplus::bus::match_t resetButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002890 power_control::dbusGPIOMatcher(resetButtonConfig,
2891 resetButtonHandler);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002892 }
2893
2894 // Request NMI_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302895 if (nmiButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302896 {
Priyatharshan P70120512020-09-16 18:47:20 +05302897 if (!nmiButtonConfig.lineName.empty())
2898 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002899 requestGPIOEvents(nmiButtonConfig.lineName, nmiButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302900 nmiButtonLine, nmiButtonEvent);
2901 }
2902 }
2903 else if (nmiButtonConfig.type == ConfigType::DBUS)
2904 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002905 static sdbusplus::bus::match_t nmiButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002906 power_control::dbusGPIOMatcher(nmiButtonConfig, nmiButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302907 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002908
2909 // Request ID_BUTTON GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302910 if (idButtonConfig.type == ConfigType::GPIO)
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302911 {
Priyatharshan P70120512020-09-16 18:47:20 +05302912 if (!idButtonConfig.lineName.empty())
2913 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002914 requestGPIOEvents(idButtonConfig.lineName, idButtonHandler,
Priyatharshan P70120512020-09-16 18:47:20 +05302915 idButtonLine, idButtonEvent);
2916 }
2917 }
2918 else if (idButtonConfig.type == ConfigType::DBUS)
2919 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002920 static sdbusplus::bus::match_t idButtonEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002921 power_control::dbusGPIOMatcher(idButtonConfig, idButtonHandler);
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302922 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002923
Jason M. Billsfb957332021-01-28 13:18:46 -08002924#ifdef USE_PLT_RST
Patrick Williams439b9c32022-07-22 19:26:53 -05002925 sdbusplus::bus::match_t pltRstMatch(
Lei YU92caa4c2021-02-23 16:59:25 +08002926 *conn,
Jason M. Billsfb957332021-01-28 13:18:46 -08002927 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
2928 "PropertiesChanged',arg0='xyz.openbmc_project.State.Host.Misc'",
Lei YU92caa4c2021-02-23 16:59:25 +08002929 hostMiscHandler);
Jason M. Billsfb957332021-01-28 13:18:46 -08002930#endif
2931
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002932 // Request POST_COMPLETE GPIO events
Priyatharshan P70120512020-09-16 18:47:20 +05302933 if (postCompleteConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002934 {
Zev Weiss676ef2c2021-09-02 21:54:02 -05002935 if (!requestGPIOEvents(postCompleteConfig.lineName, postCompleteHandler,
2936 postCompleteLine, postCompleteEvent))
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302937 {
2938 return -1;
2939 }
2940 }
Priyatharshan P70120512020-09-16 18:47:20 +05302941 else if (postCompleteConfig.type == ConfigType::DBUS)
2942 {
Patrick Williams439b9c32022-07-22 19:26:53 -05002943 static sdbusplus::bus::match_t postCompleteEventMonitor =
Zev Weiss584aa132021-09-02 19:21:52 -05002944 power_control::dbusGPIOMatcher(postCompleteConfig,
2945 postCompleteHandler);
Priyatharshan P70120512020-09-16 18:47:20 +05302946 }
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302947 else
2948 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002949 lg2::error(
Jason M. Bills41a49aa2021-03-03 16:03:25 -08002950 "postComplete name should be configured from json config file");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002951 return -1;
2952 }
2953
2954 // initialize NMI_OUT GPIO.
Priyatharshan P70120512020-09-16 18:47:20 +05302955 if (!nmiOutConfig.lineName.empty())
2956 {
Jian Zhang461a1662022-09-22 11:29:01 +08002957 setGPIOOutput(nmiOutConfig.lineName, !nmiOutConfig.polarity,
2958 nmiOutLine);
Priyatharshan P70120512020-09-16 18:47:20 +05302959 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002960
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002961 // Initialize POWER_OUT and RESET_OUT GPIO.
2962 gpiod::line line;
Priyatharshan P70120512020-09-16 18:47:20 +05302963 if (!powerOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002964 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002965 if (!setGPIOOutput(powerOutConfig.lineName, !powerOutConfig.polarity,
2966 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302967 {
2968 return -1;
2969 }
2970 }
2971 else
2972 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002973 lg2::error("powerOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002974 return -1;
2975 }
2976
Priyatharshan P70120512020-09-16 18:47:20 +05302977 if (!resetOutConfig.lineName.empty())
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002978 {
Jean-Marie Verdun50937e72021-08-31 09:15:49 -07002979 if (!setGPIOOutput(resetOutConfig.lineName, !resetOutConfig.polarity,
2980 line))
Priyatharshan P70120512020-09-16 18:47:20 +05302981 {
2982 return -1;
2983 }
2984 }
2985 else
2986 {
Jason M. Billsc46ebb42021-11-10 11:41:31 -08002987 lg2::error("ResetOut name should be configured from json config file");
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002988 return -1;
2989 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002990 // Release line
2991 line.reset();
2992
Matt Simmering58e379d2022-09-23 14:45:50 -07002993 // Initialize the power state and operating system state
Lei YU92caa4c2021-02-23 16:59:25 +08002994 powerState = PowerState::off;
Matt Simmering58e379d2022-09-23 14:45:50 -07002995 operatingSystemState = OperatingSystemStateStage::Inactive;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002996 // Check power good
Priyatharshan P70120512020-09-16 18:47:20 +05302997
2998 if (powerOkConfig.type == ConfigType::GPIO)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002999 {
Jean-Marie Verdun61b4a5b2021-09-19 08:53:28 -04003000 if (psPowerOKLine.get_value() > 0 ||
Lei YUa37c2472021-09-26 15:57:12 +08003001 (sioEnabled &&
3002 (sioPowerGoodLine.get_value() == sioPwrGoodConfig.polarity)))
Priyatharshan P70120512020-09-16 18:47:20 +05303003 {
3004 powerState = PowerState::on;
3005 }
3006 }
3007 else
3008 {
3009 if (getProperty(powerOkConfig))
3010 {
3011 powerState = PowerState::on;
3012 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003013 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003014 // Check if we need to start the Power Restore policy
Andrei Kartashev99e8f9d2022-01-09 12:15:05 +03003015 if (powerState != PowerState::on)
3016 {
3017 powerRestore.run();
3018 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003019
Lei YU92caa4c2021-02-23 16:59:25 +08003020 if (nmiOutLine)
3021 nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003022
Jason M. Billsc46ebb42021-11-10 11:41:31 -08003023 lg2::info("Initializing power state.");
Lei YU92caa4c2021-02-23 16:59:25 +08003024 logStateTransition(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003025
3026 // Power Control Service
3027 sdbusplus::asio::object_server hostServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003028 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003029
3030 // Power Control Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303031 hostIface =
3032 hostServer.add_interface("/xyz/openbmc_project/state/host" + node,
3033 "xyz.openbmc_project.State.Host");
Vernon Maueryb4d03b12021-05-26 19:11:41 -07003034 // Interface for IPMI/Redfish initiated host state transitions
Lei YU92caa4c2021-02-23 16:59:25 +08003035 hostIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003036 "RequestedHostTransition",
3037 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
3038 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003039 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003040 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003041 // if power button is masked, ignore this
3042 if (!powerButtonMask)
3043 {
3044 sendPowerControlEvent(Event::gracefulPowerOffRequest);
3045 addRestartCause(RestartCause::command);
3046 }
3047 else
3048 {
3049 lg2::info("Power Button Masked.");
3050 throw std::invalid_argument("Transition Request Masked");
3051 return 0;
3052 }
3053 }
3054 else if (requested ==
3055 "xyz.openbmc_project.State.Host.Transition.On")
3056 {
3057 // if power button is masked, ignore this
3058 if (!powerButtonMask)
3059 {
3060 sendPowerControlEvent(Event::powerOnRequest);
3061 addRestartCause(RestartCause::command);
3062 }
3063 else
3064 {
3065 lg2::info("Power Button Masked.");
3066 throw std::invalid_argument("Transition Request Masked");
3067 return 0;
3068 }
3069 }
3070 else if (requested ==
3071 "xyz.openbmc_project.State.Host.Transition.Reboot")
3072 {
3073 // if power button is masked, ignore this
3074 if (!powerButtonMask)
3075 {
3076 sendPowerControlEvent(Event::powerCycleRequest);
3077 addRestartCause(RestartCause::command);
3078 }
3079 else
3080 {
3081 lg2::info("Power Button Masked.");
3082 throw std::invalid_argument("Transition Request Masked");
3083 return 0;
3084 }
3085 }
3086 else if (
3087 requested ==
3088 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot")
3089 {
3090 // if reset button is masked, ignore this
3091 if (!resetButtonMask)
3092 {
3093 sendPowerControlEvent(Event::gracefulPowerCycleRequest);
3094 addRestartCause(RestartCause::command);
3095 }
3096 else
3097 {
3098 lg2::info("Reset Button Masked.");
3099 throw std::invalid_argument("Transition Request Masked");
3100 return 0;
3101 }
3102 }
3103 else if (
3104 requested ==
3105 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot")
3106 {
3107 // if reset button is masked, ignore this
3108 if (!resetButtonMask)
3109 {
3110 sendPowerControlEvent(Event::resetRequest);
3111 addRestartCause(RestartCause::command);
3112 }
3113 else
3114 {
3115 lg2::info("Reset Button Masked.");
3116 throw std::invalid_argument("Transition Request Masked");
3117 return 0;
3118 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08003119 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003120 else
3121 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003122 lg2::error("Unrecognized host state transition request.");
3123 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003124 return 0;
3125 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003126 resp = requested;
3127 return 1;
3128 });
Lei YU92caa4c2021-02-23 16:59:25 +08003129 hostIface->register_property("CurrentHostState",
3130 std::string(getHostState(powerState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003131
Lei YU92caa4c2021-02-23 16:59:25 +08003132 hostIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003133
3134 // Chassis Control Service
3135 sdbusplus::asio::object_server chassisServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003136 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003137
3138 // Chassis Control Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003139 chassisIface =
Priyatharshan P70120512020-09-16 18:47:20 +05303140 chassisServer.add_interface("/xyz/openbmc_project/state/chassis" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003141 "xyz.openbmc_project.State.Chassis");
3142
Lei YU92caa4c2021-02-23 16:59:25 +08003143 chassisIface->register_property(
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003144 "RequestedPowerTransition",
3145 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
3146 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003147 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003148 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003149 // if power button is masked, ignore this
3150 if (!powerButtonMask)
3151 {
3152 sendPowerControlEvent(Event::powerOffRequest);
3153 addRestartCause(RestartCause::command);
3154 }
3155 else
3156 {
3157 lg2::info("Power Button Masked.");
3158 throw std::invalid_argument("Transition Request Masked");
3159 return 0;
3160 }
3161 }
3162 else if (requested ==
3163 "xyz.openbmc_project.State.Chassis.Transition.On")
3164 {
3165 // if power button is masked, ignore this
3166 if (!powerButtonMask)
3167 {
3168 sendPowerControlEvent(Event::powerOnRequest);
3169 addRestartCause(RestartCause::command);
3170 }
3171 else
3172 {
3173 lg2::info("Power Button Masked.");
3174 throw std::invalid_argument("Transition Request Masked");
3175 return 0;
3176 }
3177 }
3178 else if (requested ==
3179 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3180 {
3181 // if power button is masked, ignore this
3182 if (!powerButtonMask)
3183 {
3184 sendPowerControlEvent(Event::powerCycleRequest);
3185 addRestartCause(RestartCause::command);
3186 }
3187 else
3188 {
3189 lg2::info("Power Button Masked.");
3190 throw std::invalid_argument("Transition Request Masked");
3191 return 0;
3192 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003193 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003194 else
3195 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003196 lg2::error("Unrecognized chassis state transition request.");
3197 throw std::invalid_argument("Unrecognized Transition Request");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003198 return 0;
3199 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003200 resp = requested;
3201 return 1;
3202 });
Lei YU92caa4c2021-02-23 16:59:25 +08003203 chassisIface->register_property("CurrentPowerState",
3204 std::string(getChassisState(powerState)));
3205 chassisIface->register_property("LastStateChangeTime", getCurrentTimeMs());
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003206
Lei YU92caa4c2021-02-23 16:59:25 +08003207 chassisIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003208
Vijay Khemka04175c22020-10-09 14:28:11 -07003209#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003210 // Chassis System Service
3211 sdbusplus::asio::object_server chassisSysServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003212 sdbusplus::asio::object_server(conn);
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003213
3214 // Chassis System Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003215 chassisSysIface = chassisSysServer.add_interface(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003216 "/xyz/openbmc_project/state/chassis_system0",
3217 "xyz.openbmc_project.State.Chassis");
3218
Lei YU92caa4c2021-02-23 16:59:25 +08003219 chassisSysIface->register_property(
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003220 "RequestedPowerTransition",
3221 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3222 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003223 if (requested ==
3224 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3225 {
3226 systemReset();
3227 addRestartCause(RestartCause::command);
3228 }
3229 else
3230 {
3231 lg2::error(
3232 "Unrecognized chassis system state transition request.");
3233 throw std::invalid_argument("Unrecognized Transition Request");
3234 return 0;
3235 }
3236 resp = requested;
3237 return 1;
3238 });
Lei YU92caa4c2021-02-23 16:59:25 +08003239 chassisSysIface->register_property(
3240 "CurrentPowerState", std::string(getChassisState(powerState)));
3241 chassisSysIface->register_property("LastStateChangeTime",
3242 getCurrentTimeMs());
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003243
Lei YU92caa4c2021-02-23 16:59:25 +08003244 chassisSysIface->initialize();
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07003245
Naveen Moses117c34e2021-05-26 20:10:51 +05303246 if (!slotPowerConfig.lineName.empty())
3247 {
3248 if (!setGPIOOutput(slotPowerConfig.lineName, 1, slotPowerLine))
3249 {
3250 return -1;
3251 }
3252
3253 slotPowerState = SlotPowerState::off;
3254 if (slotPowerLine.get_value() > 0)
3255 {
3256 slotPowerState = SlotPowerState::on;
3257 }
3258
3259 chassisSlotIface = chassisSysServer.add_interface(
3260 "/xyz/openbmc_project/state/chassis_system" + node,
3261 "xyz.openbmc_project.State.Chassis");
3262 chassisSlotIface->register_property(
3263 "RequestedPowerTransition",
3264 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
3265 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003266 if (requested ==
3267 "xyz.openbmc_project.State.Chassis.Transition.On")
3268 {
3269 slotPowerOn();
3270 }
3271 else if (requested ==
3272 "xyz.openbmc_project.State.Chassis.Transition.Off")
3273 {
3274 slotPowerOff();
3275 }
3276 else if (
3277 requested ==
3278 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
3279 {
3280 slotPowerCycle();
3281 }
3282 else
3283 {
3284 lg2::error(
3285 "Unrecognized chassis system state transition request.\n");
3286 throw std::invalid_argument(
3287 "Unrecognized Transition Request");
3288 return 0;
3289 }
3290 resp = requested;
3291 return 1;
3292 });
Naveen Moses117c34e2021-05-26 20:10:51 +05303293 chassisSlotIface->register_property(
3294 "CurrentPowerState", std::string(getSlotState(slotPowerState)));
3295 chassisSlotIface->register_property("LastStateChangeTime",
3296 getCurrentTimeMs());
3297 chassisSlotIface->initialize();
3298 }
3299#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003300 // Buttons Service
3301 sdbusplus::asio::object_server buttonsServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003302 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003303
Priyatharshan P70120512020-09-16 18:47:20 +05303304 if (!powerButtonConfig.lineName.empty())
John Wang6c090072020-09-30 13:32:16 +08003305 {
Priyatharshan P70120512020-09-16 18:47:20 +05303306 // Power Button Interface
3307 power_control::powerButtonIface = buttonsServer.add_interface(
3308 "/xyz/openbmc_project/chassis/buttons/power",
3309 "xyz.openbmc_project.Chassis.Buttons");
3310
3311 powerButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003312 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003313 if (requested)
Priyatharshan P70120512020-09-16 18:47:20 +05303314 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003315 if (powerButtonMask)
3316 {
3317 return 1;
3318 }
3319 if (!setGPIOOutput(powerOutConfig.lineName,
3320 !powerOutConfig.polarity,
3321 powerButtonMask))
3322 {
3323 throw std::runtime_error("Failed to request GPIO");
3324 return 0;
3325 }
3326 lg2::info("Power Button Masked.");
Priyatharshan P70120512020-09-16 18:47:20 +05303327 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003328 else
Priyatharshan P70120512020-09-16 18:47:20 +05303329 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003330 if (!powerButtonMask)
3331 {
3332 return 1;
3333 }
3334 lg2::info("Power Button Un-masked");
3335 powerButtonMask.reset();
Priyatharshan P70120512020-09-16 18:47:20 +05303336 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003337 // Update the mask setting
3338 current = requested;
3339 return 1;
3340 });
Priyatharshan P70120512020-09-16 18:47:20 +05303341
3342 // Check power button state
3343 bool powerButtonPressed;
3344 if (powerButtonConfig.type == ConfigType::GPIO)
3345 {
3346 powerButtonPressed = powerButtonLine.get_value() == 0;
3347 }
3348 else
3349 {
3350 powerButtonPressed = getProperty(powerButtonConfig) == 0;
3351 }
3352
3353 powerButtonIface->register_property("ButtonPressed",
3354 powerButtonPressed);
3355
3356 powerButtonIface->initialize();
3357 }
3358
3359 if (!resetButtonConfig.lineName.empty())
3360 {
3361 // Reset Button Interface
3362
Lei YU92caa4c2021-02-23 16:59:25 +08003363 resetButtonIface = buttonsServer.add_interface(
John Wang6c090072020-09-30 13:32:16 +08003364 "/xyz/openbmc_project/chassis/buttons/reset",
3365 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003366
Lei YU92caa4c2021-02-23 16:59:25 +08003367 resetButtonIface->register_property(
Patrick Williamsd394c882023-10-20 11:18:44 -05003368 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003369 if (requested)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003370 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003371 if (resetButtonMask)
3372 {
3373 return 1;
3374 }
3375 if (!setGPIOOutput(resetOutConfig.lineName,
3376 !resetOutConfig.polarity,
3377 resetButtonMask))
3378 {
3379 throw std::runtime_error("Failed to request GPIO");
3380 return 0;
3381 }
3382 lg2::info("Reset Button Masked.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003383 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003384 else
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003385 {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003386 if (!resetButtonMask)
3387 {
3388 return 1;
3389 }
3390 lg2::info("Reset Button Un-masked");
3391 resetButtonMask.reset();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003392 }
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003393 // Update the mask setting
3394 current = requested;
3395 return 1;
3396 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003397
John Wang6c090072020-09-30 13:32:16 +08003398 // Check reset button state
Priyatharshan P70120512020-09-16 18:47:20 +05303399 bool resetButtonPressed;
3400 if (resetButtonConfig.type == ConfigType::GPIO)
3401 {
3402 resetButtonPressed = resetButtonLine.get_value() == 0;
3403 }
3404 else
3405 {
3406 resetButtonPressed = getProperty(resetButtonConfig) == 0;
3407 }
3408
Lei YU92caa4c2021-02-23 16:59:25 +08003409 resetButtonIface->register_property("ButtonPressed",
3410 resetButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003411
Lei YU92caa4c2021-02-23 16:59:25 +08003412 resetButtonIface->initialize();
John Wang6c090072020-09-30 13:32:16 +08003413 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003414
Lei YU92caa4c2021-02-23 16:59:25 +08003415 if (nmiButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003416 {
3417 // NMI Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003418 nmiButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003419 "/xyz/openbmc_project/chassis/buttons/nmi",
3420 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003421
Lei YU92caa4c2021-02-23 16:59:25 +08003422 nmiButtonIface->register_property(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003423 "ButtonMasked", false, [](const bool requested, bool& current) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003424 if (nmiButtonMasked == requested)
3425 {
3426 // NMI button mask is already set as requested, so no change
3427 return 1;
3428 }
3429 if (requested)
3430 {
3431 lg2::info("NMI Button Masked.");
3432 nmiButtonMasked = true;
3433 }
3434 else
3435 {
3436 lg2::info("NMI Button Un-masked.");
3437 nmiButtonMasked = false;
3438 }
3439 // Update the mask setting
3440 current = nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003441 return 1;
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003442 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003443
Vijay Khemka33a532d2019-11-14 16:50:35 -08003444 // Check NMI button state
Priyatharshan P70120512020-09-16 18:47:20 +05303445 bool nmiButtonPressed;
3446 if (nmiButtonConfig.type == ConfigType::GPIO)
3447 {
3448 nmiButtonPressed = nmiButtonLine.get_value() == 0;
3449 }
3450 else
3451 {
3452 nmiButtonPressed = getProperty(nmiButtonConfig) == 0;
3453 }
3454
Lei YU92caa4c2021-02-23 16:59:25 +08003455 nmiButtonIface->register_property("ButtonPressed", nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003456
Lei YU92caa4c2021-02-23 16:59:25 +08003457 nmiButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003458 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003459
Lei YU92caa4c2021-02-23 16:59:25 +08003460 if (nmiOutLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003461 {
3462 // NMI out Service
3463 sdbusplus::asio::object_server nmiOutServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003464 sdbusplus::asio::object_server(conn);
Chen Yugang174ec662019-08-19 19:58:49 +08003465
Vijay Khemka33a532d2019-11-14 16:50:35 -08003466 // NMI out Interface
Priyatharshan P70120512020-09-16 18:47:20 +05303467 nmiOutIface = nmiOutServer.add_interface(
3468 "/xyz/openbmc_project/control/host" + node + "/nmi",
3469 "xyz.openbmc_project.Control.Host.NMI");
Lei YU92caa4c2021-02-23 16:59:25 +08003470 nmiOutIface->register_method("NMI", nmiReset);
3471 nmiOutIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003472 }
Chen Yugang174ec662019-08-19 19:58:49 +08003473
Lei YU92caa4c2021-02-23 16:59:25 +08003474 if (idButtonLine)
Vijay Khemka33a532d2019-11-14 16:50:35 -08003475 {
3476 // ID Button Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003477 idButtonIface = buttonsServer.add_interface(
Vijay Khemka33a532d2019-11-14 16:50:35 -08003478 "/xyz/openbmc_project/chassis/buttons/id",
3479 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003480
Vijay Khemka33a532d2019-11-14 16:50:35 -08003481 // Check ID button state
Priyatharshan P70120512020-09-16 18:47:20 +05303482 bool idButtonPressed;
3483 if (idButtonConfig.type == ConfigType::GPIO)
3484 {
3485 idButtonPressed = idButtonLine.get_value() == 0;
3486 }
3487 else
3488 {
3489 idButtonPressed = getProperty(idButtonConfig) == 0;
3490 }
3491
Lei YU92caa4c2021-02-23 16:59:25 +08003492 idButtonIface->register_property("ButtonPressed", idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003493
Lei YU92caa4c2021-02-23 16:59:25 +08003494 idButtonIface->initialize();
Vijay Khemka33a532d2019-11-14 16:50:35 -08003495 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003496
3497 // OS State Service
3498 sdbusplus::asio::object_server osServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003499 sdbusplus::asio::object_server(conn);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003500
3501 // OS State Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003502 osIface = osServer.add_interface(
Potin Lai33737912024-02-23 09:53:47 +08003503 "/xyz/openbmc_project/state/host" + node,
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003504 "xyz.openbmc_project.State.OperatingSystem.Status");
3505
3506 // Get the initial OS state based on POST complete
Jason M. Billsc6d75652024-09-10 16:54:26 -07003507 // Asserted, OS state is "Standby" (ready to boot)
3508 // De-Asserted, OS state is "Inactive"
Tim Lee86239182021-12-23 11:46:01 +08003509 OperatingSystemStateStage osState;
Priyatharshan P70120512020-09-16 18:47:20 +05303510 if (postCompleteConfig.type == ConfigType::GPIO)
3511 {
Jason M. Billsc6d75652024-09-10 16:54:26 -07003512 osState = postCompleteLine.get_value() == postCompleteConfig.polarity
3513 ? OperatingSystemStateStage::Standby
3514 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303515 }
3516 else
3517 {
Tim Lee86239182021-12-23 11:46:01 +08003518 osState = getProperty(postCompleteConfig) > 0
Jason M. Billsc6d75652024-09-10 16:54:26 -07003519 ? OperatingSystemStateStage::Standby
3520 : OperatingSystemStateStage::Inactive;
Priyatharshan P70120512020-09-16 18:47:20 +05303521 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003522
Tim Lee86239182021-12-23 11:46:01 +08003523 osIface->register_property(
3524 "OperatingSystemState",
3525 std::string(getOperatingSystemStateStage(osState)));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003526
Lei YU92caa4c2021-02-23 16:59:25 +08003527 osIface->initialize();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003528
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003529 // Restart Cause Service
3530 sdbusplus::asio::object_server restartCauseServer =
Lei YU92caa4c2021-02-23 16:59:25 +08003531 sdbusplus::asio::object_server(conn);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003532
3533 // Restart Cause Interface
Lei YU92caa4c2021-02-23 16:59:25 +08003534 restartCauseIface = restartCauseServer.add_interface(
Naveen Mosesec972d82021-07-16 21:19:23 +05303535 "/xyz/openbmc_project/control/host" + node + "/restart_cause",
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003536 "xyz.openbmc_project.Control.Host.RestartCause");
3537
Lei YU92caa4c2021-02-23 16:59:25 +08003538 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003539 "RestartCause",
3540 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
3541
Lei YU92caa4c2021-02-23 16:59:25 +08003542 restartCauseIface->register_property(
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003543 "RequestedRestartCause",
3544 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
3545 [](const std::string& requested, std::string& resp) {
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003546 if (requested ==
3547 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
3548 {
3549 addRestartCause(RestartCause::watchdog);
3550 }
3551 else
3552 {
3553 throw std::invalid_argument(
3554 "Unrecognized RestartCause Request");
3555 return 0;
3556 }
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003557
Patrick Williamsa0a39f82024-08-16 15:20:19 -04003558 lg2::info("RestartCause requested: {RESTART_CAUSE}",
3559 "RESTART_CAUSE", requested);
3560 resp = requested;
3561 return 1;
3562 });
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003563
Lei YU92caa4c2021-02-23 16:59:25 +08003564 restartCauseIface->initialize();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07003565
Lei YU92caa4c2021-02-23 16:59:25 +08003566 currentHostStateMonitor();
Yong Li8d660212019-12-27 10:18:10 +08003567
Konstantin Aladyshevcfc4d252021-11-18 11:08:38 +03003568 if (!hpmStbyEnConfig.lineName.empty())
3569 {
3570 // Set to indicate BMC's power control module is ready to take
3571 // the inputs [PWR_GOOD] from the HPM FPGA
3572 gpiod::line hpmLine;
3573 if (!setGPIOOutput(hpmStbyEnConfig.lineName, hpmStbyEnConfig.polarity,
3574 hpmLine))
3575 {
3576 return -1;
3577 }
3578 }
3579
Lei YU92caa4c2021-02-23 16:59:25 +08003580 io.run();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07003581
3582 return 0;
3583}