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