blob: dce34407baad8c05b3d4f70cc2fda97ed4fff626 [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*/
16#include "i2c.hpp"
17
18#include <sys/sysinfo.h>
19#include <systemd/sd-journal.h>
20
21#include <boost/asio/posix/stream_descriptor.hpp>
22#include <boost/container/flat_map.hpp>
23#include <filesystem>
24#include <fstream>
25#include <gpiod.hpp>
26#include <iostream>
27#include <sdbusplus/asio/object_server.hpp>
28#include <string_view>
29
30namespace power_control
31{
32static boost::asio::io_service io;
33std::shared_ptr<sdbusplus::asio::connection> conn;
34static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
35static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
36static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
37static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
38static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
39static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
40static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
41
42static gpiod::line powerButtonMask;
43static gpiod::line resetButtonMask;
44static bool nmiButtonMasked = false;
45static bool resetInProgress = false;
46
47const static constexpr int powerPulseTimeMs = 200;
48const static constexpr int forceOffPulseTimeMs = 15000;
49const static constexpr int resetPulseTimeMs = 500;
50const static constexpr int powerCycleTimeMs = 1000;
51const static constexpr int sioPowerGoodWatchdogTimeMs = 1000;
52const static constexpr int psPowerOKWatchdogTimeMs = 8000;
53const static constexpr int gracefulPowerOffTimeMs = 60000;
54const static constexpr int buttonMaskTimeMs = 60000;
55const static constexpr int powerOffSaveTimeMs = 7000;
56
57const static std::filesystem::path powerControlDir = "/var/lib/power-control";
58const static constexpr std::string_view powerStateFile = "power-state";
59
60static bool nmiEnabled = true;
61static constexpr const char* nmiOutName = "NMI_OUT";
62
63// Timers
64// Time holding GPIOs asserted
65static boost::asio::steady_timer gpioAssertTimer(io);
66// Time between off and on during a power cycle
67static boost::asio::steady_timer powerCycleTimer(io);
68// Time OS gracefully powering off
69static boost::asio::steady_timer gracefulPowerOffTimer(io);
70// Time power supply power OK assertion on power-on
71static boost::asio::steady_timer psPowerOKWatchdogTimer(io);
72// Time SIO power good assertion on power-on
73static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
74// Time power-off state save for power loss tracking
75static boost::asio::steady_timer powerStateSaveTimer(io);
76// POH timer
77static boost::asio::steady_timer pohCounterTimer(io);
78
79// GPIO Lines and Event Descriptors
80static gpiod::line psPowerOKLine;
81static boost::asio::posix::stream_descriptor psPowerOKEvent(io);
82static gpiod::line sioPowerGoodLine;
83static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
84static gpiod::line sioOnControlLine;
85static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
86static gpiod::line sioS5Line;
87static boost::asio::posix::stream_descriptor sioS5Event(io);
88static gpiod::line powerButtonLine;
89static boost::asio::posix::stream_descriptor powerButtonEvent(io);
90static gpiod::line resetButtonLine;
91static boost::asio::posix::stream_descriptor resetButtonEvent(io);
92static gpiod::line nmiButtonLine;
93static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
94static gpiod::line idButtonLine;
95static boost::asio::posix::stream_descriptor idButtonEvent(io);
96static gpiod::line postCompleteLine;
97static boost::asio::posix::stream_descriptor postCompleteEvent(io);
98static gpiod::line nmiOutLine;
99
100static constexpr uint8_t beepPowerFail = 8;
101
102static void beep(const uint8_t& beepPriority)
103{
104 std::cerr << "Beep with priority: " << (unsigned)beepPriority << "\n";
105
106 conn->async_method_call(
107 [](boost::system::error_code ec) {
108 if (ec)
109 {
110 std::cerr << "beep returned error with "
111 "async_method_call (ec = "
112 << ec << ")\n";
113 return;
114 }
115 },
116 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
117 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
118}
119
120enum class PowerState
121{
122 on,
123 waitForPSPowerOK,
124 waitForSIOPowerGood,
125 failedTransitionToOn,
126 off,
127 transitionToOff,
128 gracefulTransitionToOff,
129 cycleOff,
130 transitionToCycleOff,
131 gracefulTransitionToCycleOff,
132};
133static PowerState powerState;
134static std::string getPowerStateName(PowerState state)
135{
136 switch (state)
137 {
138 case PowerState::on:
139 return "On";
140 break;
141 case PowerState::waitForPSPowerOK:
142 return "Wait for Power Supply Power OK";
143 break;
144 case PowerState::waitForSIOPowerGood:
145 return "Wait for SIO Power Good";
146 break;
147 case PowerState::failedTransitionToOn:
148 return "Failed Transition to On";
149 break;
150 case PowerState::off:
151 return "Off";
152 break;
153 case PowerState::transitionToOff:
154 return "Transition to Off";
155 break;
156 case PowerState::gracefulTransitionToOff:
157 return "Graceful Transition to Off";
158 break;
159 case PowerState::cycleOff:
160 return "Power Cycle Off";
161 break;
162 case PowerState::transitionToCycleOff:
163 return "Transition to Power Cycle Off";
164 break;
165 case PowerState::gracefulTransitionToCycleOff:
166 return "Graceful Transition to Power Cycle Off";
167 break;
168 default:
169 return "unknown state: " + std::to_string(static_cast<int>(state));
170 break;
171 }
172}
173static void logStateTransition(const PowerState state)
174{
175 std::cerr << "Moving to \"" << getPowerStateName(state) << "\" state.\n";
176}
177
178enum class Event
179{
180 psPowerOKAssert,
181 psPowerOKDeAssert,
182 sioPowerGoodAssert,
183 sioPowerGoodDeAssert,
184 sioS5Assert,
185 sioS5DeAssert,
186 powerButtonPressed,
187 powerCycleTimerExpired,
188 psPowerOKWatchdogTimerExpired,
189 sioPowerGoodWatchdogTimerExpired,
190 gracefulPowerOffTimerExpired,
191 powerOnRequest,
192 powerOffRequest,
193 powerCycleRequest,
194 resetRequest,
195 gracefulPowerOffRequest,
196 gracefulPowerCycleRequest,
197};
198static std::string getEventName(Event event)
199{
200 switch (event)
201 {
202 case Event::psPowerOKAssert:
203 return "power supply power OK assert";
204 break;
205 case Event::psPowerOKDeAssert:
206 return "power supply power OK de-assert";
207 break;
208 case Event::sioPowerGoodAssert:
209 return "SIO power good assert";
210 break;
211 case Event::sioPowerGoodDeAssert:
212 return "SIO power good de-assert";
213 break;
214 case Event::sioS5Assert:
215 return "SIO S5 assert";
216 break;
217 case Event::sioS5DeAssert:
218 return "SIO S5 de-assert";
219 break;
220 case Event::powerButtonPressed:
221 return "power button pressed";
222 break;
223 case Event::powerCycleTimerExpired:
224 return "power cycle timer expired";
225 break;
226 case Event::psPowerOKWatchdogTimerExpired:
227 return "power supply power OK watchdog timer expired";
228 break;
229 case Event::sioPowerGoodWatchdogTimerExpired:
230 return "SIO power good watchdog timer expired";
231 break;
232 case Event::gracefulPowerOffTimerExpired:
233 return "graceful power-off timer expired";
234 break;
235 case Event::powerOnRequest:
236 return "power-on request";
237 break;
238 case Event::powerOffRequest:
239 return "power-off request";
240 break;
241 case Event::powerCycleRequest:
242 return "power-cycle request";
243 break;
244 case Event::resetRequest:
245 return "reset request";
246 break;
247 case Event::gracefulPowerOffRequest:
248 return "graceful power-off request";
249 break;
250 case Event::gracefulPowerCycleRequest:
251 return "graceful power-cycle request";
252 break;
253 default:
254 return "unknown event: " + std::to_string(static_cast<int>(event));
255 break;
256 }
257}
258static void logEvent(const std::string_view stateHandler, const Event event)
259{
260 std::cerr << stateHandler << ": " << getEventName(event)
261 << " event received.\n";
262}
263
264// Power state handlers
265static void powerStateOn(const Event event);
266static void powerStateWaitForPSPowerOK(const Event event);
267static void powerStateWaitForSIOPowerGood(const Event event);
268static void powerStateFailedTransitionToOn(const Event event);
269static void powerStateOff(const Event event);
270static void powerStateTransitionToOff(const Event event);
271static void powerStateGracefulTransitionToOff(const Event event);
272static void powerStateCycleOff(const Event event);
273static void powerStateTransitionToCycleOff(const Event event);
274static void powerStateGracefulTransitionToCycleOff(const Event event);
275
276static std::function<void(const Event)> getPowerStateHandler(PowerState state)
277{
278 switch (state)
279 {
280 case PowerState::on:
281 return powerStateOn;
282 break;
283 case PowerState::waitForPSPowerOK:
284 return powerStateWaitForPSPowerOK;
285 break;
286 case PowerState::waitForSIOPowerGood:
287 return powerStateWaitForSIOPowerGood;
288 break;
289 case PowerState::failedTransitionToOn:
290 return powerStateFailedTransitionToOn;
291 break;
292 case PowerState::off:
293 return powerStateOff;
294 break;
295 case PowerState::transitionToOff:
296 return powerStateTransitionToOff;
297 break;
298 case PowerState::gracefulTransitionToOff:
299 return powerStateGracefulTransitionToOff;
300 break;
301 case PowerState::cycleOff:
302 return powerStateCycleOff;
303 break;
304 case PowerState::transitionToCycleOff:
305 return powerStateTransitionToCycleOff;
306 break;
307 case PowerState::gracefulTransitionToCycleOff:
308 return powerStateGracefulTransitionToCycleOff;
309 break;
310 default:
311 return std::function<void(const Event)>{};
312 break;
313 }
314};
315
316static void sendPowerControlEvent(const Event event)
317{
318 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
319 if (handler == nullptr)
320 {
321 std::cerr << "Failed to find handler for power state: "
322 << static_cast<int>(powerState) << "\n";
323 return;
324 }
325 handler(event);
326}
327
328static uint64_t getCurrentTimeMs()
329{
330 struct timespec time = {};
331
332 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
333 {
334 return 0;
335 }
336 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
337 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
338
339 return currentTimeMs;
340}
341
342static constexpr std::string_view getHostState(const PowerState state)
343{
344 switch (state)
345 {
346 case PowerState::on:
347 case PowerState::transitionToOff:
348 case PowerState::gracefulTransitionToOff:
349 case PowerState::transitionToCycleOff:
350 case PowerState::gracefulTransitionToCycleOff:
351 return "xyz.openbmc_project.State.Host.HostState.Running";
352 break;
353 case PowerState::waitForPSPowerOK:
354 case PowerState::waitForSIOPowerGood:
355 case PowerState::failedTransitionToOn:
356 case PowerState::off:
357 case PowerState::cycleOff:
358 return "xyz.openbmc_project.State.Host.HostState.Off";
359 break;
360 default:
361 return "";
362 break;
363 }
364};
365static constexpr std::string_view getChassisState(const PowerState state)
366{
367 switch (state)
368 {
369 case PowerState::on:
370 case PowerState::transitionToOff:
371 case PowerState::gracefulTransitionToOff:
372 case PowerState::transitionToCycleOff:
373 case PowerState::gracefulTransitionToCycleOff:
374 return "xyz.openbmc_project.State.Chassis.PowerState.On";
375 break;
376 case PowerState::waitForPSPowerOK:
377 case PowerState::waitForSIOPowerGood:
378 case PowerState::failedTransitionToOn:
379 case PowerState::off:
380 case PowerState::cycleOff:
381 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
382 break;
383 default:
384 return "";
385 break;
386 }
387};
388static void savePowerState(const PowerState state)
389{
390 powerStateSaveTimer.expires_after(
391 std::chrono::milliseconds(powerOffSaveTimeMs));
392 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
393 if (ec)
394 {
395 // operation_aborted is expected if timer is canceled before
396 // completion.
397 if (ec != boost::asio::error::operation_aborted)
398 {
399 std::cerr << "Power-state save async_wait failed: "
400 << ec.message() << "\n";
401 }
402 return;
403 }
404 std::ofstream powerStateStream(powerControlDir / powerStateFile);
405 powerStateStream << getChassisState(state);
406 });
407}
408static void setPowerState(const PowerState state)
409{
410 powerState = state;
411 logStateTransition(state);
412
413 hostIface->set_property("CurrentHostState",
414 std::string(getHostState(powerState)));
415
416 chassisIface->set_property("CurrentPowerState",
417 std::string(getChassisState(powerState)));
418 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
419
420 // Save the power state for the restore policy
421 savePowerState(state);
422}
423
424enum class RestartCause
425{
426 command,
427 resetButton,
428 powerButton,
429 powerPolicyOn,
430 powerPolicyRestore,
431 softReset,
432};
433static std::string getRestartCause(RestartCause cause)
434{
435 switch (cause)
436 {
437 case RestartCause::command:
438 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
439 break;
440 case RestartCause::resetButton:
441 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
442 break;
443 case RestartCause::powerButton:
444 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
445 break;
446 case RestartCause::powerPolicyOn:
447 return "xyz.openbmc_project.State.Host.RestartCause."
448 "PowerPolicyAlwaysOn";
449 break;
450 case RestartCause::powerPolicyRestore:
451 return "xyz.openbmc_project.State.Host.RestartCause."
452 "PowerPolicyPreviousState";
453 break;
454 case RestartCause::softReset:
455 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
456 break;
457 default:
458 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
459 break;
460 }
461}
462static void setRestartCause(const RestartCause cause)
463{
464 conn->async_method_call(
465 [](boost::system::error_code ec) {
466 if (ec)
467 {
468 std::cerr << "failed to set RestartCause\n";
469 }
470 },
471 "xyz.openbmc_project.Settings",
472 "/xyz/openbmc_project/control/host0/restart_cause",
473 "org.freedesktop.DBus.Properties", "Set",
474 "xyz.openbmc_project.Common.RestartCause", "RestartCause",
475 std::variant<std::string>(getRestartCause(cause)));
476}
477
478static void powerRestorePolicyLog()
479{
480 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
481 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
482 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
483}
484
485static void powerButtonPressLog()
486{
487 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
488 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
489 "OpenBMC.0.1.PowerButtonPressed", NULL);
490}
491
492static void resetButtonPressLog()
493{
494 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
495 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
496 "OpenBMC.0.1.ResetButtonPressed", NULL);
497}
498
499static void nmiButtonPressLog()
500{
501 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
502 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
503 "OpenBMC.0.1.NMIButtonPressed", NULL);
504}
505
506static void nmiDiagIntLog()
507{
508 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
509 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
510 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
511}
512
513static int initializePowerStateStorage()
514{
515 // create the power control directory if it doesn't exist
516 std::error_code ec;
517 if (!(std::filesystem::create_directories(powerControlDir, ec)))
518 {
519 if (ec.value() != 0)
520 {
521 std::cerr << "failed to create " << powerControlDir << ": "
522 << ec.message() << "\n";
523 return -1;
524 }
525 }
526 // Create the power state file if it doesn't exist
527 if (!std::filesystem::exists(powerControlDir / powerStateFile))
528 {
529 std::ofstream powerStateStream(powerControlDir / powerStateFile);
530 powerStateStream << getChassisState(powerState);
531 }
532 return 0;
533}
534
535static bool wasPowerDropped()
536{
537 std::ifstream powerStateStream(powerControlDir / powerStateFile);
538 if (!powerStateStream.is_open())
539 {
540 std::cerr << "Failed to open power state file\n";
541 return false;
542 }
543
544 std::string state;
545 std::getline(powerStateStream, state);
546 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
547}
548
549static void invokePowerRestorePolicy(const std::string& policy)
550{
551 // Async events may call this twice, but we only want to run once
552 static bool policyInvoked = false;
553 if (policyInvoked)
554 {
555 return;
556 }
557 policyInvoked = true;
558
559 std::cerr << "Power restore delay expired, invoking " << policy << "\n";
560 if (policy ==
561 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
562 {
563 sendPowerControlEvent(Event::powerOnRequest);
564 setRestartCause(RestartCause::powerPolicyOn);
565 }
566 else if (policy == "xyz.openbmc_project.Control.Power.RestorePolicy."
567 "Policy.Restore")
568 {
569 if (wasPowerDropped())
570 {
571 std::cerr << "Power was dropped, restoring Host On state\n";
572 sendPowerControlEvent(Event::powerOnRequest);
573 setRestartCause(RestartCause::powerPolicyRestore);
574 }
575 else
576 {
577 std::cerr << "No power drop, restoring Host Off state\n";
578 }
579 }
580}
581
582static void powerRestorePolicyDelay(int delay)
583{
584 // Async events may call this twice, but we only want to run once
585 static bool delayStarted = false;
586 if (delayStarted)
587 {
588 return;
589 }
590 delayStarted = true;
591 // Calculate the delay from now to meet the requested delay
592 // Subtract the approximate uboot time
593 static constexpr const int ubootSeconds = 20;
594 delay -= ubootSeconds;
595 // Subtract the time since boot
596 struct sysinfo info = {};
597 if (sysinfo(&info) == 0)
598 {
599 delay -= info.uptime;
600 }
601 // 0 is the minimum delay
602 delay = std::max(delay, 0);
603
604 static boost::asio::steady_timer powerRestorePolicyTimer(io);
605 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
606 std::cerr << "Power restore delay of " << delay << " seconds started\n";
607 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
608 if (ec)
609 {
610 // operation_aborted is expected if timer is canceled before
611 // completion.
612 if (ec != boost::asio::error::operation_aborted)
613 {
614 std::cerr << "power restore policy async_wait failed: "
615 << ec.message() << "\n";
616 }
617 return;
618 }
619 // Get Power Restore Policy
620 // In case PowerRestorePolicy is not available, set a match for it
621 static std::unique_ptr<sdbusplus::bus::match::match>
622 powerRestorePolicyMatch = std::make_unique<
623 sdbusplus::bus::match::match>(
624 *conn,
625 "type='signal',interface='org.freedesktop.DBus.Properties',"
626 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
627 "project.Control.Power.RestorePolicy'",
628 [](sdbusplus::message::message& msg) {
629 std::string interfaceName;
630 boost::container::flat_map<std::string,
631 std::variant<std::string>>
632 propertiesChanged;
633 std::string policy;
634 try
635 {
636 msg.read(interfaceName, propertiesChanged);
637 policy = std::get<std::string>(
638 propertiesChanged.begin()->second);
639 }
640 catch (std::exception& e)
641 {
642 std::cerr
643 << "Unable to read power restore policy value\n";
644 powerRestorePolicyMatch.reset();
645 return;
646 }
647 invokePowerRestorePolicy(policy);
648 powerRestorePolicyMatch.reset();
649 });
650
651 // Check if it's already on DBus
652 conn->async_method_call(
653 [](boost::system::error_code ec,
654 const std::variant<std::string>& policyProperty) {
655 if (ec)
656 {
657 return;
658 }
659 powerRestorePolicyMatch.reset();
660 const std::string* policy =
661 std::get_if<std::string>(&policyProperty);
662 if (policy == nullptr)
663 {
664 std::cerr << "Unable to read power restore policy value\n";
665 return;
666 }
667 invokePowerRestorePolicy(*policy);
668 },
669 "xyz.openbmc_project.Settings",
670 "/xyz/openbmc_project/control/host0/power_restore_policy",
671 "org.freedesktop.DBus.Properties", "Get",
672 "xyz.openbmc_project.Control.Power.RestorePolicy",
673 "PowerRestorePolicy");
674 });
675}
676
677static void powerRestorePolicyStart()
678{
679 std::cerr << "Power restore policy started\n";
680 powerRestorePolicyLog();
681
682 // Get the desired delay time
683 // In case PowerRestoreDelay is not available, set a match for it
684 static std::unique_ptr<sdbusplus::bus::match::match>
685 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
686 *conn,
687 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
688 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
689 "Power.RestoreDelay'",
690 [](sdbusplus::message::message& msg) {
691 std::string interfaceName;
692 boost::container::flat_map<std::string, std::variant<uint16_t>>
693 propertiesChanged;
694 int delay = 0;
695 try
696 {
697 msg.read(interfaceName, propertiesChanged);
698 delay =
699 std::get<uint16_t>(propertiesChanged.begin()->second);
700 }
701 catch (std::exception& e)
702 {
703 std::cerr << "Unable to read power restore delay value\n";
704 powerRestoreDelayMatch.reset();
705 return;
706 }
707 powerRestorePolicyDelay(delay);
708 powerRestoreDelayMatch.reset();
709 });
710
711 // Check if it's already on DBus
712 conn->async_method_call(
713 [](boost::system::error_code ec,
714 const std::variant<uint16_t>& delayProperty) {
715 if (ec)
716 {
717 return;
718 }
719 powerRestoreDelayMatch.reset();
720 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
721 if (delay == nullptr)
722 {
723 std::cerr << "Unable to read power restore delay value\n";
724 return;
725 }
726 powerRestorePolicyDelay(*delay);
727 },
728 "xyz.openbmc_project.Settings",
729 "/xyz/openbmc_project/control/power_restore_delay",
730 "org.freedesktop.DBus.Properties", "Get",
731 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
732}
733
734static void powerRestorePolicyCheck()
735{
736 // In case ACBoot is not available, set a match for it
737 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
738 std::make_unique<sdbusplus::bus::match::match>(
739 *conn,
740 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
741 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
742 "ACBoot'",
743 [](sdbusplus::message::message& msg) {
744 std::string interfaceName;
745 boost::container::flat_map<std::string,
746 std::variant<std::string>>
747 propertiesChanged;
748 std::string acBoot;
749 try
750 {
751 msg.read(interfaceName, propertiesChanged);
752 acBoot = std::get<std::string>(
753 propertiesChanged.begin()->second);
754 }
755 catch (std::exception& e)
756 {
757 std::cerr << "Unable to read AC Boot status\n";
758 acBootMatch.reset();
759 return;
760 }
761 if (acBoot == "Unknown")
762 {
763 return;
764 }
765 if (acBoot == "True")
766 {
767 // Start the Power Restore policy
768 powerRestorePolicyStart();
769 }
770 acBootMatch.reset();
771 });
772
773 // Check if it's already on DBus
774 conn->async_method_call(
775 [](boost::system::error_code ec,
776 const std::variant<std::string>& acBootProperty) {
777 if (ec)
778 {
779 return;
780 }
781 const std::string* acBoot =
782 std::get_if<std::string>(&acBootProperty);
783 if (acBoot == nullptr)
784 {
785 std::cerr << "Unable to read AC Boot status\n";
786 return;
787 }
788 if (*acBoot == "Unknown")
789 {
790 return;
791 }
792 if (*acBoot == "True")
793 {
794 // Start the Power Restore policy
795 powerRestorePolicyStart();
796 }
797 acBootMatch.reset();
798 },
799 "xyz.openbmc_project.Settings",
800 "/xyz/openbmc_project/control/host0/ac_boot",
801 "org.freedesktop.DBus.Properties", "Get",
802 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
803}
804
805static bool requestGPIOEvents(
806 const std::string& name, const std::function<void()>& handler,
807 gpiod::line& gpioLine,
808 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
809{
810 // Find the GPIO line
811 gpioLine = gpiod::find_line(name);
812 if (!gpioLine)
813 {
814 std::cerr << "Failed to find the " << name << " line\n";
815 return false;
816 }
817
818 try
819 {
820 gpioLine.request(
821 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
822 }
823 catch (std::exception&)
824 {
825 std::cerr << "Failed to request events for " << name << "\n";
826 return false;
827 }
828
829 int gpioLineFd = gpioLine.event_get_fd();
830 if (gpioLineFd < 0)
831 {
832 std::cerr << "Failed to get " << name << " fd\n";
833 return false;
834 }
835
836 gpioEventDescriptor.assign(gpioLineFd);
837
838 gpioEventDescriptor.async_wait(
839 boost::asio::posix::stream_descriptor::wait_read,
840 [&name, handler](const boost::system::error_code ec) {
841 if (ec)
842 {
843 std::cerr << name << " fd handler error: " << ec.message()
844 << "\n";
845 // TODO: throw here to force power-control to restart?
846 return;
847 }
848 handler();
849 });
850 return true;
851}
852
853static bool setGPIOOutput(const std::string& name, const int value,
854 gpiod::line& gpioLine)
855{
856 // Find the GPIO line
857 gpioLine = gpiod::find_line(name);
858 if (!gpioLine)
859 {
860 std::cerr << "Failed to find the " << name << " line.\n";
861 return false;
862 }
863
864 // Request GPIO output to specified value
865 try
866 {
867 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
868 value);
869 }
870 catch (std::exception&)
871 {
872 std::cerr << "Failed to request " << name << " output\n";
873 return false;
874 }
875
876 std::cerr << name << " set to " << std::to_string(value) << "\n";
877 return true;
878}
879
880static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
881 const std::string& name, const int value,
882 const int durationMs)
883{
884 // Set the masked GPIO line to the specified value
885 maskedGPIOLine.set_value(value);
886 std::cerr << name << " set to " << std::to_string(value) << "\n";
887 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
888 gpioAssertTimer.async_wait(
889 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
890 // Set the masked GPIO line back to the opposite value
891 maskedGPIOLine.set_value(!value);
892 std::cerr << name << " released\n";
893 if (ec)
894 {
895 // operation_aborted is expected if timer is canceled before
896 // completion.
897 if (ec != boost::asio::error::operation_aborted)
898 {
899 std::cerr << name << " async_wait failed: " + ec.message()
900 << "\n";
901 }
902 }
903 });
904 return 0;
905}
906
907static int setGPIOOutputForMs(const std::string& name, const int value,
908 const int durationMs)
909{
910 // If the requested GPIO is masked, use the mask line to set the output
911 if (powerButtonMask && name == "POWER_OUT")
912 {
913 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
914 durationMs);
915 }
916 if (resetButtonMask && name == "RESET_OUT")
917 {
918 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
919 durationMs);
920 }
921
922 // No mask set, so request and set the GPIO normally
923 gpiod::line gpioLine;
924 if (!setGPIOOutput(name, value, gpioLine))
925 {
926 return -1;
927 }
928 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
929 gpioAssertTimer.async_wait(
930 [gpioLine, name](const boost::system::error_code ec) {
931 std::cerr << name << " released\n";
932 if (ec)
933 {
934 // operation_aborted is expected if timer is canceled before
935 // completion.
936 if (ec != boost::asio::error::operation_aborted)
937 {
938 std::cerr << name << " async_wait failed: " << ec.message()
939 << "\n";
940 }
941 }
942 });
943 return 0;
944}
945
946static void powerOn()
947{
948 setGPIOOutputForMs("POWER_OUT", 0, powerPulseTimeMs);
949}
950
951static void gracefulPowerOff()
952{
953 setGPIOOutputForMs("POWER_OUT", 0, powerPulseTimeMs);
954}
955
956static void forcePowerOff()
957{
958 if (setGPIOOutputForMs("POWER_OUT", 0, forceOffPulseTimeMs) < 0)
959 {
960 return;
961 }
962
963 // If the force off timer expires, then the PCH power-button override
964 // failed, so attempt the Unconditional Powerdown SMBus command.
965 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
966 if (ec)
967 {
968 // operation_aborted is expected if timer is canceled before
969 // completion.
970 if (ec != boost::asio::error::operation_aborted)
971 {
972 std::cerr << "Force power off async_wait failed: "
973 << ec.message() << "\n";
974 }
975 return;
976 }
977 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
978 "Powerdown SMBus command.\n";
979 const static constexpr size_t pchDevBusAddress = 3;
980 const static constexpr size_t pchDevSlaveAddress = 0x44;
981 const static constexpr size_t pchCmdReg = 0;
982 const static constexpr size_t pchPowerDownCmd = 0x02;
983 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
984 pchPowerDownCmd) < 0)
985 {
986 std::cerr << "Unconditional Powerdown command failed! Not sure "
987 "what to do now.\n";
988 }
989 });
990}
991
992static void reset()
993{
994 setGPIOOutputForMs("RESET_OUT", 0, resetPulseTimeMs);
995}
996
997static void gracefulPowerOffTimerStart()
998{
999 std::cerr << "Graceful power-off timer started\n";
1000 gracefulPowerOffTimer.expires_after(
1001 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1002 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1003 if (ec)
1004 {
1005 // operation_aborted is expected if timer is canceled before
1006 // completion.
1007 if (ec != boost::asio::error::operation_aborted)
1008 {
1009 std::cerr << "Graceful power-off async_wait failed: "
1010 << ec.message() << "\n";
1011 }
1012 std::cerr << "Graceful power-off timer canceled\n";
1013 return;
1014 }
1015 std::cerr << "Graceful power-off timer completed\n";
1016 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1017 });
1018}
1019
1020static void powerCycleTimerStart()
1021{
1022 std::cerr << "Power-cycle timer started\n";
1023 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1024 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1025 if (ec)
1026 {
1027 // operation_aborted is expected if timer is canceled before
1028 // completion.
1029 if (ec != boost::asio::error::operation_aborted)
1030 {
1031 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1032 << "\n";
1033 }
1034 std::cerr << "Power-cycle timer canceled\n";
1035 return;
1036 }
1037 std::cerr << "Power-cycle timer completed\n";
1038 sendPowerControlEvent(Event::powerCycleTimerExpired);
1039 });
1040}
1041
1042static void psPowerOKWatchdogTimerStart()
1043{
1044 std::cerr << "power supply power OK watchdog timer started\n";
1045 psPowerOKWatchdogTimer.expires_after(
1046 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1047 psPowerOKWatchdogTimer.async_wait(
1048 [](const boost::system::error_code ec) {
1049 if (ec)
1050 {
1051 // operation_aborted is expected if timer is canceled before
1052 // completion.
1053 if (ec != boost::asio::error::operation_aborted)
1054 {
1055 std::cerr
1056 << "power supply power OK watchdog async_wait failed: "
1057 << ec.message() << "\n";
1058 }
1059 std::cerr << "power supply power OK watchdog timer canceled\n";
1060 return;
1061 }
1062 std::cerr << "power supply power OK watchdog timer expired\n";
1063 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1064 });
1065}
1066
1067static void pohCounterTimerStart()
1068{
1069 std::cerr << "POH timer started\n";
1070 // Set the time-out as 1 hour, to align with POH command in ipmid
1071 pohCounterTimer.expires_after(std::chrono::hours(1));
1072 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1073 if (ec)
1074 {
1075 // operation_aborted is expected if timer is canceled before
1076 // completion.
1077 if (ec != boost::asio::error::operation_aborted)
1078 {
1079 std::cerr << "POH timer async_wait failed: " << ec.message()
1080 << "\n";
1081 }
1082 std::cerr << "POH timer canceled\n";
1083 return;
1084 }
1085
1086 if (getHostState(powerState) !=
1087 "xyz.openbmc_project.State.Host.HostState.Running")
1088 {
1089 return;
1090 }
1091
1092 conn->async_method_call(
1093 [](boost::system::error_code ec,
1094 const std::variant<uint32_t>& pohCounterProperty) {
1095 if (ec)
1096 {
1097 std::cerr << "error to get poh counter\n";
1098 return;
1099 }
1100 const uint32_t* pohCounter =
1101 std::get_if<uint32_t>(&pohCounterProperty);
1102 if (pohCounter == nullptr)
1103 {
1104 std::cerr << "unable to read poh counter\n";
1105 return;
1106 }
1107
1108 conn->async_method_call(
1109 [](boost::system::error_code ec) {
1110 if (ec)
1111 {
1112 std::cerr << "failed to set poh counter\n";
1113 }
1114 },
1115 "xyz.openbmc_project.Settings",
1116 "/xyz/openbmc_project/state/chassis0",
1117 "org.freedesktop.DBus.Properties", "Set",
1118 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1119 std::variant<uint32_t>(*pohCounter + 1));
1120 },
1121 "xyz.openbmc_project.Settings",
1122 "/xyz/openbmc_project/state/chassis0",
1123 "org.freedesktop.DBus.Properties", "Get",
1124 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1125
1126 pohCounterTimerStart();
1127 });
1128}
1129
1130static void currentHostStateMonitor()
1131{
1132 static auto match = sdbusplus::bus::match::match(
1133 *conn,
1134 "type='signal',member='PropertiesChanged', "
1135 "interface='org.freedesktop.DBus.Properties', "
1136 "arg0namespace='xyz.openbmc_project.State.Host'",
1137 [](sdbusplus::message::message& message) {
1138 std::string intfName;
1139 std::map<std::string, std::variant<std::string>> properties;
1140
1141 message.read(intfName, properties);
1142
1143 std::variant<std::string> currentHostState;
1144
1145 try
1146 {
1147 currentHostState = properties.at("CurrentHostState");
1148 }
1149 catch (const std::out_of_range& e)
1150 {
1151 std::cerr << "Error in finding CurrentHostState property\n";
1152
1153 return;
1154 }
1155
1156 if (std::get<std::string>(currentHostState) ==
1157 "xyz.openbmc_project.State.Host.HostState.Running")
1158 {
1159 pohCounterTimerStart();
1160 }
1161 else
1162 {
1163 pohCounterTimer.cancel();
1164 }
1165 });
1166}
1167
1168static void sioPowerGoodWatchdogTimerStart()
1169{
1170 std::cerr << "SIO power good watchdog timer started\n";
1171 sioPowerGoodWatchdogTimer.expires_after(
1172 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1173 sioPowerGoodWatchdogTimer.async_wait(
1174 [](const boost::system::error_code ec) {
1175 if (ec)
1176 {
1177 // operation_aborted is expected if timer is canceled before
1178 // completion.
1179 if (ec != boost::asio::error::operation_aborted)
1180 {
1181 std::cerr << "SIO power good watchdog async_wait failed: "
1182 << ec.message() << "\n";
1183 }
1184 std::cerr << "SIO power good watchdog timer canceled\n";
1185 return;
1186 }
1187 std::cerr << "SIO power good watchdog timer completed\n";
1188 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1189 });
1190}
1191
1192static void powerStateOn(const Event event)
1193{
1194 logEvent(__FUNCTION__, event);
1195 switch (event)
1196 {
1197 case Event::psPowerOKDeAssert:
1198 setPowerState(PowerState::off);
1199 // DC power is unexpectedly lost, beep
1200 beep(beepPowerFail);
1201 break;
1202 case Event::sioS5Assert:
1203 setPowerState(PowerState::transitionToOff);
1204 setRestartCause(RestartCause::softReset);
1205 break;
1206 case Event::powerButtonPressed:
1207 setPowerState(PowerState::gracefulTransitionToOff);
1208 gracefulPowerOffTimerStart();
1209 break;
1210 case Event::powerOffRequest:
1211 setPowerState(PowerState::transitionToOff);
1212 forcePowerOff();
1213 break;
1214 case Event::gracefulPowerOffRequest:
1215 setPowerState(PowerState::gracefulTransitionToOff);
1216 gracefulPowerOffTimerStart();
1217 gracefulPowerOff();
1218 break;
1219 case Event::powerCycleRequest:
1220 setPowerState(PowerState::transitionToCycleOff);
1221 forcePowerOff();
1222 break;
1223 case Event::gracefulPowerCycleRequest:
1224 setPowerState(PowerState::gracefulTransitionToCycleOff);
1225 gracefulPowerOffTimerStart();
1226 gracefulPowerOff();
1227 break;
1228 case Event::resetRequest:
1229 reset();
1230 break;
1231 default:
1232 std::cerr << "No action taken.\n";
1233 break;
1234 }
1235}
1236
1237static void powerStateWaitForPSPowerOK(const Event event)
1238{
1239 logEvent(__FUNCTION__, event);
1240 switch (event)
1241 {
1242 case Event::psPowerOKAssert:
1243 // Cancel any GPIO assertions held during the transition
1244 gpioAssertTimer.cancel();
1245 psPowerOKWatchdogTimer.cancel();
1246 sioPowerGoodWatchdogTimerStart();
1247 setPowerState(PowerState::waitForSIOPowerGood);
1248 break;
1249 case Event::psPowerOKWatchdogTimerExpired:
1250 setPowerState(PowerState::failedTransitionToOn);
1251 break;
1252 default:
1253 std::cerr << "No action taken.\n";
1254 break;
1255 }
1256}
1257
1258static void powerStateWaitForSIOPowerGood(const Event event)
1259{
1260 logEvent(__FUNCTION__, event);
1261 switch (event)
1262 {
1263 case Event::sioPowerGoodAssert:
1264 sioPowerGoodWatchdogTimer.cancel();
1265 setPowerState(PowerState::on);
1266 break;
1267 case Event::sioPowerGoodWatchdogTimerExpired:
1268 setPowerState(PowerState::failedTransitionToOn);
1269 forcePowerOff();
1270 break;
1271 default:
1272 std::cerr << "No action taken.\n";
1273 break;
1274 }
1275}
1276
1277static void powerStateFailedTransitionToOn(const Event event)
1278{
1279 logEvent(__FUNCTION__, event);
1280 switch (event)
1281 {
1282 case Event::psPowerOKAssert:
1283 // We're in a failure state, so don't allow the system to turn on
1284 // without a user request
1285 forcePowerOff();
1286 break;
1287 case Event::psPowerOKDeAssert:
1288 // Cancel any GPIO assertions held during the transition
1289 gpioAssertTimer.cancel();
1290 break;
1291 case Event::powerButtonPressed:
1292 psPowerOKWatchdogTimerStart();
1293 setPowerState(PowerState::waitForPSPowerOK);
1294 break;
1295 case Event::powerOnRequest:
1296 psPowerOKWatchdogTimerStart();
1297 setPowerState(PowerState::waitForPSPowerOK);
1298 powerOn();
1299 break;
1300 default:
1301 std::cerr << "No action taken.\n";
1302 break;
1303 }
1304}
1305
1306static void powerStateOff(const Event event)
1307{
1308 logEvent(__FUNCTION__, event);
1309 switch (event)
1310 {
1311 case Event::psPowerOKAssert:
1312 setPowerState(PowerState::waitForSIOPowerGood);
1313 break;
1314 case Event::sioS5DeAssert:
1315 setPowerState(PowerState::waitForPSPowerOK);
1316 break;
1317 case Event::powerButtonPressed:
1318 psPowerOKWatchdogTimerStart();
1319 setPowerState(PowerState::waitForPSPowerOK);
1320 break;
1321 case Event::powerOnRequest:
1322 psPowerOKWatchdogTimerStart();
1323 setPowerState(PowerState::waitForPSPowerOK);
1324 powerOn();
1325 break;
1326 default:
1327 std::cerr << "No action taken.\n";
1328 break;
1329 }
1330}
1331
1332static void powerStateTransitionToOff(const Event event)
1333{
1334 logEvent(__FUNCTION__, event);
1335 switch (event)
1336 {
1337 case Event::psPowerOKDeAssert:
1338 // Cancel any GPIO assertions held during the transition
1339 gpioAssertTimer.cancel();
1340 setPowerState(PowerState::off);
1341 break;
1342 default:
1343 std::cerr << "No action taken.\n";
1344 break;
1345 }
1346}
1347
1348static void powerStateGracefulTransitionToOff(const Event event)
1349{
1350 logEvent(__FUNCTION__, event);
1351 switch (event)
1352 {
1353 case Event::psPowerOKDeAssert:
1354 gracefulPowerOffTimer.cancel();
1355 setPowerState(PowerState::off);
1356 break;
1357 case Event::gracefulPowerOffTimerExpired:
1358 setPowerState(PowerState::on);
1359 break;
1360 default:
1361 std::cerr << "No action taken.\n";
1362 break;
1363 }
1364}
1365
1366static void powerStateCycleOff(const Event event)
1367{
1368 logEvent(__FUNCTION__, event);
1369 switch (event)
1370 {
1371 case Event::powerCycleTimerExpired:
1372 psPowerOKWatchdogTimerStart();
1373 setPowerState(PowerState::waitForPSPowerOK);
1374 powerOn();
1375 break;
1376 default:
1377 std::cerr << "No action taken.\n";
1378 break;
1379 }
1380}
1381
1382static void powerStateTransitionToCycleOff(const Event event)
1383{
1384 logEvent(__FUNCTION__, event);
1385 switch (event)
1386 {
1387 case Event::psPowerOKDeAssert:
1388 // Cancel any GPIO assertions held during the transition
1389 gpioAssertTimer.cancel();
1390 setPowerState(PowerState::cycleOff);
1391 powerCycleTimerStart();
1392 break;
1393 default:
1394 std::cerr << "No action taken.\n";
1395 break;
1396 }
1397}
1398
1399static void powerStateGracefulTransitionToCycleOff(const Event event)
1400{
1401 logEvent(__FUNCTION__, event);
1402 switch (event)
1403 {
1404 case Event::psPowerOKDeAssert:
1405 gracefulPowerOffTimer.cancel();
1406 setPowerState(PowerState::cycleOff);
1407 powerCycleTimerStart();
1408 break;
1409 case Event::gracefulPowerOffTimerExpired:
1410 setPowerState(PowerState::on);
1411 break;
1412 default:
1413 std::cerr << "No action taken.\n";
1414 break;
1415 }
1416}
1417
1418static void psPowerOKHandler()
1419{
1420 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1421
1422 Event powerControlEvent =
1423 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1424 ? Event::psPowerOKAssert
1425 : Event::psPowerOKDeAssert;
1426
1427 sendPowerControlEvent(powerControlEvent);
1428 psPowerOKEvent.async_wait(
1429 boost::asio::posix::stream_descriptor::wait_read,
1430 [](const boost::system::error_code ec) {
1431 if (ec)
1432 {
1433 std::cerr << "power supply power OK handler error: "
1434 << ec.message() << "\n";
1435 return;
1436 }
1437 psPowerOKHandler();
1438 });
1439}
1440
1441static void sioPowerGoodHandler()
1442{
1443 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1444
1445 Event powerControlEvent =
1446 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1447 ? Event::sioPowerGoodAssert
1448 : Event::sioPowerGoodDeAssert;
1449
1450 sendPowerControlEvent(powerControlEvent);
1451 sioPowerGoodEvent.async_wait(
1452 boost::asio::posix::stream_descriptor::wait_read,
1453 [](const boost::system::error_code ec) {
1454 if (ec)
1455 {
1456 std::cerr << "SIO power good handler error: " << ec.message()
1457 << "\n";
1458 return;
1459 }
1460 sioPowerGoodHandler();
1461 });
1462}
1463
1464static void sioOnControlHandler()
1465{
1466 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1467
1468 bool sioOnControl =
1469 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1470 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1471 sioOnControlEvent.async_wait(
1472 boost::asio::posix::stream_descriptor::wait_read,
1473 [](const boost::system::error_code ec) {
1474 if (ec)
1475 {
1476 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1477 << "\n";
1478 return;
1479 }
1480 sioOnControlHandler();
1481 });
1482}
1483
1484static void sioS5Handler()
1485{
1486 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1487
1488 Event powerControlEvent =
1489 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1490 ? Event::sioS5Assert
1491 : Event::sioS5DeAssert;
1492
1493 sendPowerControlEvent(powerControlEvent);
1494 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1495 [](const boost::system::error_code ec) {
1496 if (ec)
1497 {
1498 std::cerr << "SIO S5 handler error: "
1499 << ec.message() << "\n";
1500 return;
1501 }
1502 sioS5Handler();
1503 });
1504}
1505
1506static void powerButtonHandler()
1507{
1508 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1509
1510 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1511 {
1512 powerButtonPressLog();
1513 powerButtonIface->set_property("ButtonPressed", true);
1514 if (!powerButtonMask)
1515 {
1516 sendPowerControlEvent(Event::powerButtonPressed);
1517 setRestartCause(RestartCause::powerButton);
1518 }
1519 else
1520 {
1521 std::cerr << "power button press masked\n";
1522 }
1523 }
1524 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1525 {
1526 powerButtonIface->set_property("ButtonPressed", false);
1527 }
1528 powerButtonEvent.async_wait(
1529 boost::asio::posix::stream_descriptor::wait_read,
1530 [](const boost::system::error_code ec) {
1531 if (ec)
1532 {
1533 std::cerr << "power button handler error: " << ec.message()
1534 << "\n";
1535 return;
1536 }
1537 powerButtonHandler();
1538 });
1539}
1540
1541static void resetButtonHandler()
1542{
1543 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1544
1545 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1546 {
1547 resetButtonPressLog();
1548 resetButtonIface->set_property("ButtonPressed", true);
1549 if (!resetButtonMask)
1550 {
1551 resetInProgress = true;
1552 setRestartCause(RestartCause::resetButton);
1553 }
1554 else
1555 {
1556 std::cerr << "reset button press masked\n";
1557 }
1558 }
1559 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1560 {
1561 resetButtonIface->set_property("ButtonPressed", false);
1562 }
1563 resetButtonEvent.async_wait(
1564 boost::asio::posix::stream_descriptor::wait_read,
1565 [](const boost::system::error_code ec) {
1566 if (ec)
1567 {
1568 std::cerr << "reset button handler error: " << ec.message()
1569 << "\n";
1570 return;
1571 }
1572 resetButtonHandler();
1573 });
1574}
1575
1576static void nmiSetEnablePorperty(bool value)
1577{
1578 conn->async_method_call(
1579 [](boost::system::error_code ec) {
1580 if (ec)
1581 {
1582 std::cerr << "failed to set NMI source\n";
1583 }
1584 },
1585 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1586 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1587 "Enabled", std::variant<bool>{value});
1588}
1589
1590static void nmiReset(void)
1591{
1592 static constexpr const uint8_t value = 1;
1593 const static constexpr int nmiOutPulseTimeMs = 200;
1594
1595 std::cerr << "NMI out action \n";
1596 nmiOutLine.set_value(value);
1597 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1598 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1599 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1600 // restore the NMI_OUT GPIO line back to the opposite value
1601 nmiOutLine.set_value(!value);
1602 std::cerr << nmiOutName << " released\n";
1603 if (ec)
1604 {
1605 // operation_aborted is expected if timer is canceled before
1606 // completion.
1607 if (ec != boost::asio::error::operation_aborted)
1608 {
1609 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1610 << "\n";
1611 }
1612 }
1613 });
1614 // log to redfish
1615 nmiDiagIntLog();
1616 std::cerr << "NMI out action completed\n";
1617 // reset Enable Property
1618 nmiSetEnablePorperty(false);
1619}
1620
1621static void nmiSourcePropertyMonitor(void)
1622{
1623 std::cerr << " NMI Source Property Monitor \n";
1624
1625 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1626 std::make_unique<sdbusplus::bus::match::match>(
1627 *conn,
1628 "type='signal',interface='org.freedesktop.DBus.Properties',"
1629 "member='PropertiesChanged',arg0namespace='com.intel.Control."
1630 "NMISource'",
1631 [](sdbusplus::message::message& msg) {
1632 std::string interfaceName;
1633 boost::container::flat_map<std::string,
1634 std::variant<bool, std::string>>
1635 propertiesChanged;
1636 std::string state;
1637 bool value = true;
1638 try
1639 {
1640 msg.read(interfaceName, propertiesChanged);
1641 if (propertiesChanged.begin()->first == "Enabled")
1642 {
1643 value =
1644 std::get<bool>(propertiesChanged.begin()->second);
1645 std::cerr
1646 << " NMI Enabled propertiesChanged value: " << value
1647 << "\n";
1648 nmiEnabled = value;
1649 if (nmiEnabled)
1650 {
1651 nmiReset();
1652 }
1653 }
1654 }
1655 catch (std::exception& e)
1656 {
1657 std::cerr << "Unable to read NMI source\n";
1658 return;
1659 }
1660 });
1661}
1662
1663static void setNmiSource()
1664{
1665 conn->async_method_call(
1666 [](boost::system::error_code ec) {
1667 if (ec)
1668 {
1669 std::cerr << "failed to set NMI source\n";
1670 }
1671 },
1672 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1673 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1674 "BMCSource",
1675 std::variant<std::string>{
1676 "com.intel.Control.NMISource.BMCSourceSignal.FpBtn"});
1677 // set Enable Property
1678 nmiSetEnablePorperty(true);
1679}
1680
1681static void nmiButtonHandler()
1682{
1683 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1684
1685 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1686 {
1687 nmiButtonPressLog();
1688 nmiButtonIface->set_property("ButtonPressed", true);
1689 if (nmiButtonMasked)
1690 {
1691 std::cerr << "NMI button press masked\n";
1692 }
1693 else
1694 {
1695 setNmiSource();
1696 }
1697 }
1698 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1699 {
1700 nmiButtonIface->set_property("ButtonPressed", false);
1701 }
1702 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1703 [](const boost::system::error_code ec) {
1704 if (ec)
1705 {
1706 std::cerr << "NMI button handler error: "
1707 << ec.message() << "\n";
1708 return;
1709 }
1710 nmiButtonHandler();
1711 });
1712}
1713
1714static void idButtonHandler()
1715{
1716 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1717
1718 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1719 {
1720 idButtonIface->set_property("ButtonPressed", true);
1721 }
1722 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1723 {
1724 idButtonIface->set_property("ButtonPressed", false);
1725 }
1726 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1727 [](const boost::system::error_code& ec) {
1728 if (ec)
1729 {
1730 std::cerr << "ID button handler error: "
1731 << ec.message() << "\n";
1732 return;
1733 }
1734 idButtonHandler();
1735 });
1736}
1737
1738static void postCompleteHandler()
1739{
1740 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1741
1742 bool postComplete =
1743 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
1744 std::cerr << "POST complete value changed: " << postComplete << "\n";
1745 if (postComplete)
1746 {
1747 osIface->set_property("OperatingSystemState", std::string("Standby"));
1748 resetInProgress = false;
1749 }
1750 else
1751 {
1752 osIface->set_property("OperatingSystemState", std::string("Inactive"));
1753 // Set the restart cause if POST complete de-asserted by host software
1754 if (powerState == PowerState::on && !resetInProgress)
1755 {
1756 resetInProgress = true;
1757 setRestartCause(RestartCause::softReset);
1758 }
1759 }
1760 postCompleteEvent.async_wait(
1761 boost::asio::posix::stream_descriptor::wait_read,
1762 [](const boost::system::error_code ec) {
1763 if (ec)
1764 {
1765 std::cerr << "POST complete handler error: " << ec.message()
1766 << "\n";
1767 return;
1768 }
1769 postCompleteHandler();
1770 });
1771}
1772} // namespace power_control
1773
1774int main(int argc, char* argv[])
1775{
1776 std::cerr << "Start Chassis power control service...\n";
1777 power_control::conn =
1778 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1779
1780 // Request all the dbus names
1781 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1782 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
1783 power_control::conn->request_name(
1784 "xyz.openbmc_project.State.OperatingSystem");
1785 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
1786
1787 // Request PS_PWROK GPIO events
1788 if (!power_control::requestGPIOEvents(
1789 "PS_PWROK", power_control::psPowerOKHandler,
1790 power_control::psPowerOKLine, power_control::psPowerOKEvent))
1791 {
1792 return -1;
1793 }
1794
1795 // Request SIO_POWER_GOOD GPIO events
1796 if (!power_control::requestGPIOEvents(
1797 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
1798 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
1799 {
1800 return -1;
1801 }
1802
1803 // Request SIO_ONCONTROL GPIO events
1804 if (!power_control::requestGPIOEvents(
1805 "SIO_ONCONTROL", power_control::sioOnControlHandler,
1806 power_control::sioOnControlLine, power_control::sioOnControlEvent))
1807 {
1808 return -1;
1809 }
1810
1811 // Request SIO_S5 GPIO events
1812 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
1813 power_control::sioS5Line,
1814 power_control::sioS5Event))
1815 {
1816 return -1;
1817 }
1818
1819 // Request POWER_BUTTON GPIO events
1820 if (!power_control::requestGPIOEvents(
1821 "POWER_BUTTON", power_control::powerButtonHandler,
1822 power_control::powerButtonLine, power_control::powerButtonEvent))
1823 {
1824 return -1;
1825 }
1826
1827 // Request RESET_BUTTON GPIO events
1828 if (!power_control::requestGPIOEvents(
1829 "RESET_BUTTON", power_control::resetButtonHandler,
1830 power_control::resetButtonLine, power_control::resetButtonEvent))
1831 {
1832 return -1;
1833 }
1834
1835 // Request NMI_BUTTON GPIO events
1836 if (!power_control::requestGPIOEvents(
1837 "NMI_BUTTON", power_control::nmiButtonHandler,
1838 power_control::nmiButtonLine, power_control::nmiButtonEvent))
1839 {
1840 return -1;
1841 }
1842
1843 // Request ID_BUTTON GPIO events
1844 if (!power_control::requestGPIOEvents(
1845 "ID_BUTTON", power_control::idButtonHandler,
1846 power_control::idButtonLine, power_control::idButtonEvent))
1847 {
1848 return -1;
1849 }
1850
1851 // Request POST_COMPLETE GPIO events
1852 if (!power_control::requestGPIOEvents(
1853 "POST_COMPLETE", power_control::postCompleteHandler,
1854 power_control::postCompleteLine, power_control::postCompleteEvent))
1855 {
1856 return -1;
1857 }
1858
1859 // initialize NMI_OUT GPIO.
1860 if (!power_control::setGPIOOutput(power_control::nmiOutName, 0,
1861 power_control::nmiOutLine))
1862 {
1863 return -1;
1864 }
1865
1866 // Initialize the power state
1867 power_control::powerState = power_control::PowerState::off;
1868 // Check power good
1869 if (power_control::psPowerOKLine.get_value() > 0)
1870 {
1871 power_control::powerState = power_control::PowerState::on;
1872 }
1873
1874 // Initialize the power state storage
1875 if (power_control::initializePowerStateStorage() < 0)
1876 {
1877 return -1;
1878 }
1879
1880 // Check if we need to start the Power Restore policy
1881 power_control::powerRestorePolicyCheck();
1882
1883 power_control::nmiSourcePropertyMonitor();
1884
1885 std::cerr << "Initializing power state. ";
1886 power_control::logStateTransition(power_control::powerState);
1887
1888 // Power Control Service
1889 sdbusplus::asio::object_server hostServer =
1890 sdbusplus::asio::object_server(power_control::conn);
1891
1892 // Power Control Interface
1893 power_control::hostIface = hostServer.add_interface(
1894 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
1895
1896 power_control::hostIface->register_property(
1897 "RequestedHostTransition",
1898 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
1899 [](const std::string& requested, std::string& resp) {
1900 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
1901 {
1902 sendPowerControlEvent(
1903 power_control::Event::gracefulPowerOffRequest);
1904 }
1905 else if (requested ==
1906 "xyz.openbmc_project.State.Host.Transition.On")
1907 {
1908 sendPowerControlEvent(power_control::Event::powerOnRequest);
1909 setRestartCause(power_control::RestartCause::command);
1910 }
1911 else if (requested ==
1912 "xyz.openbmc_project.State.Host.Transition.Reboot")
1913 {
1914 sendPowerControlEvent(
1915 power_control::Event::gracefulPowerCycleRequest);
1916 setRestartCause(power_control::RestartCause::command);
1917 }
1918 else
1919 {
1920 std::cerr << "Unrecognized host state transition request.\n";
1921 throw std::invalid_argument("Unrecognized Transition Request");
1922 return 0;
1923 }
1924 resp = requested;
1925 return 1;
1926 });
1927 power_control::hostIface->register_property(
1928 "CurrentHostState",
1929 std::string(power_control::getHostState(power_control::powerState)));
1930
1931 power_control::currentHostStateMonitor();
1932
1933 power_control::hostIface->initialize();
1934
1935 // Chassis Control Service
1936 sdbusplus::asio::object_server chassisServer =
1937 sdbusplus::asio::object_server(power_control::conn);
1938
1939 // Chassis Control Interface
1940 power_control::chassisIface =
1941 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
1942 "xyz.openbmc_project.State.Chassis");
1943
1944 power_control::chassisIface->register_property(
1945 "RequestedPowerTransition",
1946 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
1947 [](const std::string& requested, std::string& resp) {
1948 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
1949 {
1950 sendPowerControlEvent(power_control::Event::powerOffRequest);
1951 }
1952 else if (requested ==
1953 "xyz.openbmc_project.State.Chassis.Transition.On")
1954 {
1955 sendPowerControlEvent(power_control::Event::powerOnRequest);
1956 setRestartCause(power_control::RestartCause::command);
1957 }
1958 else if (requested ==
1959 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
1960 {
1961 sendPowerControlEvent(power_control::Event::powerCycleRequest);
1962 setRestartCause(power_control::RestartCause::command);
1963 }
1964 else if (requested ==
1965 "xyz.openbmc_project.State.Chassis.Transition.Reset")
1966 {
1967 setRestartCause(power_control::RestartCause::command);
1968 sendPowerControlEvent(power_control::Event::resetRequest);
1969 }
1970 else
1971 {
1972 std::cerr << "Unrecognized chassis state transition request.\n";
1973 throw std::invalid_argument("Unrecognized Transition Request");
1974 return 0;
1975 }
1976 resp = requested;
1977 return 1;
1978 });
1979 power_control::chassisIface->register_property(
1980 "CurrentPowerState",
1981 std::string(power_control::getChassisState(power_control::powerState)));
1982 power_control::chassisIface->register_property(
1983 "LastStateChangeTime", power_control::getCurrentTimeMs());
1984
1985 power_control::chassisIface->initialize();
1986
1987 // Buttons Service
1988 sdbusplus::asio::object_server buttonsServer =
1989 sdbusplus::asio::object_server(power_control::conn);
1990
1991 // Power Button Interface
1992 power_control::powerButtonIface = buttonsServer.add_interface(
1993 "/xyz/openbmc_project/chassis/buttons/power",
1994 "xyz.openbmc_project.Chassis.Buttons");
1995
1996 power_control::powerButtonIface->register_property(
1997 "ButtonMasked", false, [](const bool requested, bool& current) {
1998 if (requested)
1999 {
2000 if (power_control::powerButtonMask)
2001 {
2002 return 1;
2003 }
2004 if (!power_control::setGPIOOutput(
2005 "POWER_OUT", 1, power_control::powerButtonMask))
2006 {
2007 throw std::runtime_error("Failed to request GPIO");
2008 return 0;
2009 }
2010 std::cerr << "Power Button Masked.\n";
2011 }
2012 else
2013 {
2014 if (!power_control::powerButtonMask)
2015 {
2016 return 1;
2017 }
2018 std::cerr << "Power Button Un-masked\n";
2019 power_control::powerButtonMask.reset();
2020 }
2021 // Update the mask setting
2022 current = requested;
2023 return 1;
2024 });
2025
2026 // Check power button state
2027 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2028 power_control::powerButtonIface->register_property("ButtonPressed",
2029 powerButtonPressed);
2030
2031 power_control::powerButtonIface->initialize();
2032
2033 // Reset Button Interface
2034 power_control::resetButtonIface = buttonsServer.add_interface(
2035 "/xyz/openbmc_project/chassis/buttons/reset",
2036 "xyz.openbmc_project.Chassis.Buttons");
2037
2038 power_control::resetButtonIface->register_property(
2039 "ButtonMasked", false, [](const bool requested, bool& current) {
2040 if (requested)
2041 {
2042 if (power_control::resetButtonMask)
2043 {
2044 return 1;
2045 }
2046 if (!power_control::setGPIOOutput(
2047 "RESET_OUT", 1, power_control::resetButtonMask))
2048 {
2049 throw std::runtime_error("Failed to request GPIO");
2050 return 0;
2051 }
2052 std::cerr << "Reset Button Masked.\n";
2053 }
2054 else
2055 {
2056 if (!power_control::resetButtonMask)
2057 {
2058 return 1;
2059 }
2060 std::cerr << "Reset Button Un-masked\n";
2061 power_control::resetButtonMask.reset();
2062 }
2063 // Update the mask setting
2064 current = requested;
2065 return 1;
2066 });
2067
2068 // Check reset button state
2069 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2070 power_control::resetButtonIface->register_property("ButtonPressed",
2071 resetButtonPressed);
2072
2073 power_control::resetButtonIface->initialize();
2074
2075 // NMI Button Interface
2076 power_control::nmiButtonIface =
2077 buttonsServer.add_interface("/xyz/openbmc_project/chassis/buttons/nmi",
2078 "xyz.openbmc_project.Chassis.Buttons");
2079
2080 power_control::nmiButtonIface->register_property(
2081 "ButtonMasked", false, [](const bool requested, bool& current) {
2082 if (power_control::nmiButtonMasked == requested)
2083 {
2084 // NMI button mask is already set as requested, so no change
2085 return 1;
2086 }
2087 if (requested)
2088 {
2089 std::cerr << "NMI Button Masked.\n";
2090 power_control::nmiButtonMasked = true;
2091 }
2092 else
2093 {
2094 std::cerr << "NMI Button Un-masked.\n";
2095 power_control::nmiButtonMasked = false;
2096 }
2097 // Update the mask setting
2098 current = power_control::nmiButtonMasked;
2099 return 1;
2100 });
2101
2102 // Check NMI button state
2103 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2104 power_control::nmiButtonIface->register_property("ButtonPressed",
2105 nmiButtonPressed);
2106
2107 power_control::nmiButtonIface->initialize();
2108
2109 // ID Button Interface
2110 power_control::idButtonIface =
2111 buttonsServer.add_interface("/xyz/openbmc_project/chassis/buttons/id",
2112 "xyz.openbmc_project.Chassis.Buttons");
2113
2114 // Check ID button state
2115 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2116 power_control::idButtonIface->register_property("ButtonPressed",
2117 idButtonPressed);
2118
2119 power_control::idButtonIface->initialize();
2120
2121 // OS State Service
2122 sdbusplus::asio::object_server osServer =
2123 sdbusplus::asio::object_server(power_control::conn);
2124
2125 // OS State Interface
2126 power_control::osIface = osServer.add_interface(
2127 "/xyz/openbmc_project/state/os",
2128 "xyz.openbmc_project.State.OperatingSystem.Status");
2129
2130 // Get the initial OS state based on POST complete
2131 // 0: Asserted, OS state is "Standby" (ready to boot)
2132 // 1: De-Asserted, OS state is "Inactive"
2133 std::string osState = power_control::postCompleteLine.get_value() > 0
2134 ? "Inactive"
2135 : "Standby";
2136
2137 power_control::osIface->register_property("OperatingSystemState",
2138 std::string(osState));
2139
2140 power_control::osIface->initialize();
2141
2142 power_control::io.run();
2143
2144 return 0;
2145}