blob: 2cdda2cb6e5f70d409df5fab6e9b539d490fb8a4 [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;
1257 default:
1258 std::cerr << "No action taken.\n";
1259 break;
1260 }
1261}
1262
1263static void powerStateWaitForSIOPowerGood(const Event event)
1264{
1265 logEvent(__FUNCTION__, event);
1266 switch (event)
1267 {
1268 case Event::sioPowerGoodAssert:
1269 sioPowerGoodWatchdogTimer.cancel();
1270 setPowerState(PowerState::on);
1271 break;
1272 case Event::sioPowerGoodWatchdogTimerExpired:
1273 setPowerState(PowerState::failedTransitionToOn);
1274 forcePowerOff();
1275 break;
1276 default:
1277 std::cerr << "No action taken.\n";
1278 break;
1279 }
1280}
1281
1282static void powerStateFailedTransitionToOn(const Event event)
1283{
1284 logEvent(__FUNCTION__, event);
1285 switch (event)
1286 {
1287 case Event::psPowerOKAssert:
1288 // We're in a failure state, so don't allow the system to turn on
1289 // without a user request
1290 forcePowerOff();
1291 break;
1292 case Event::psPowerOKDeAssert:
1293 // Cancel any GPIO assertions held during the transition
1294 gpioAssertTimer.cancel();
1295 break;
1296 case Event::powerButtonPressed:
1297 psPowerOKWatchdogTimerStart();
1298 setPowerState(PowerState::waitForPSPowerOK);
1299 break;
1300 case Event::powerOnRequest:
1301 psPowerOKWatchdogTimerStart();
1302 setPowerState(PowerState::waitForPSPowerOK);
1303 powerOn();
1304 break;
1305 default:
1306 std::cerr << "No action taken.\n";
1307 break;
1308 }
1309}
1310
1311static void powerStateOff(const Event event)
1312{
1313 logEvent(__FUNCTION__, event);
1314 switch (event)
1315 {
1316 case Event::psPowerOKAssert:
1317 setPowerState(PowerState::waitForSIOPowerGood);
1318 break;
1319 case Event::sioS5DeAssert:
1320 setPowerState(PowerState::waitForPSPowerOK);
1321 break;
1322 case Event::powerButtonPressed:
1323 psPowerOKWatchdogTimerStart();
1324 setPowerState(PowerState::waitForPSPowerOK);
1325 break;
1326 case Event::powerOnRequest:
1327 psPowerOKWatchdogTimerStart();
1328 setPowerState(PowerState::waitForPSPowerOK);
1329 powerOn();
1330 break;
1331 default:
1332 std::cerr << "No action taken.\n";
1333 break;
1334 }
1335}
1336
1337static void powerStateTransitionToOff(const Event event)
1338{
1339 logEvent(__FUNCTION__, event);
1340 switch (event)
1341 {
1342 case Event::psPowerOKDeAssert:
1343 // Cancel any GPIO assertions held during the transition
1344 gpioAssertTimer.cancel();
1345 setPowerState(PowerState::off);
1346 break;
1347 default:
1348 std::cerr << "No action taken.\n";
1349 break;
1350 }
1351}
1352
1353static void powerStateGracefulTransitionToOff(const Event event)
1354{
1355 logEvent(__FUNCTION__, event);
1356 switch (event)
1357 {
1358 case Event::psPowerOKDeAssert:
1359 gracefulPowerOffTimer.cancel();
1360 setPowerState(PowerState::off);
1361 break;
1362 case Event::gracefulPowerOffTimerExpired:
1363 setPowerState(PowerState::on);
1364 break;
1365 default:
1366 std::cerr << "No action taken.\n";
1367 break;
1368 }
1369}
1370
1371static void powerStateCycleOff(const Event event)
1372{
1373 logEvent(__FUNCTION__, event);
1374 switch (event)
1375 {
1376 case Event::powerCycleTimerExpired:
1377 psPowerOKWatchdogTimerStart();
1378 setPowerState(PowerState::waitForPSPowerOK);
1379 powerOn();
1380 break;
1381 default:
1382 std::cerr << "No action taken.\n";
1383 break;
1384 }
1385}
1386
1387static void powerStateTransitionToCycleOff(const Event event)
1388{
1389 logEvent(__FUNCTION__, event);
1390 switch (event)
1391 {
1392 case Event::psPowerOKDeAssert:
1393 // Cancel any GPIO assertions held during the transition
1394 gpioAssertTimer.cancel();
1395 setPowerState(PowerState::cycleOff);
1396 powerCycleTimerStart();
1397 break;
1398 default:
1399 std::cerr << "No action taken.\n";
1400 break;
1401 }
1402}
1403
1404static void powerStateGracefulTransitionToCycleOff(const Event event)
1405{
1406 logEvent(__FUNCTION__, event);
1407 switch (event)
1408 {
1409 case Event::psPowerOKDeAssert:
1410 gracefulPowerOffTimer.cancel();
1411 setPowerState(PowerState::cycleOff);
1412 powerCycleTimerStart();
1413 break;
1414 case Event::gracefulPowerOffTimerExpired:
1415 setPowerState(PowerState::on);
1416 break;
1417 default:
1418 std::cerr << "No action taken.\n";
1419 break;
1420 }
1421}
1422
1423static void psPowerOKHandler()
1424{
1425 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1426
1427 Event powerControlEvent =
1428 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1429 ? Event::psPowerOKAssert
1430 : Event::psPowerOKDeAssert;
1431
1432 sendPowerControlEvent(powerControlEvent);
1433 psPowerOKEvent.async_wait(
1434 boost::asio::posix::stream_descriptor::wait_read,
1435 [](const boost::system::error_code ec) {
1436 if (ec)
1437 {
1438 std::cerr << "power supply power OK handler error: "
1439 << ec.message() << "\n";
1440 return;
1441 }
1442 psPowerOKHandler();
1443 });
1444}
1445
1446static void sioPowerGoodHandler()
1447{
1448 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1449
1450 Event powerControlEvent =
1451 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1452 ? Event::sioPowerGoodAssert
1453 : Event::sioPowerGoodDeAssert;
1454
1455 sendPowerControlEvent(powerControlEvent);
1456 sioPowerGoodEvent.async_wait(
1457 boost::asio::posix::stream_descriptor::wait_read,
1458 [](const boost::system::error_code ec) {
1459 if (ec)
1460 {
1461 std::cerr << "SIO power good handler error: " << ec.message()
1462 << "\n";
1463 return;
1464 }
1465 sioPowerGoodHandler();
1466 });
1467}
1468
1469static void sioOnControlHandler()
1470{
1471 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1472
1473 bool sioOnControl =
1474 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1475 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1476 sioOnControlEvent.async_wait(
1477 boost::asio::posix::stream_descriptor::wait_read,
1478 [](const boost::system::error_code ec) {
1479 if (ec)
1480 {
1481 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1482 << "\n";
1483 return;
1484 }
1485 sioOnControlHandler();
1486 });
1487}
1488
1489static void sioS5Handler()
1490{
1491 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1492
1493 Event powerControlEvent =
1494 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1495 ? Event::sioS5Assert
1496 : Event::sioS5DeAssert;
1497
1498 sendPowerControlEvent(powerControlEvent);
1499 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1500 [](const boost::system::error_code ec) {
1501 if (ec)
1502 {
1503 std::cerr << "SIO S5 handler error: "
1504 << ec.message() << "\n";
1505 return;
1506 }
1507 sioS5Handler();
1508 });
1509}
1510
1511static void powerButtonHandler()
1512{
1513 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1514
1515 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1516 {
1517 powerButtonPressLog();
1518 powerButtonIface->set_property("ButtonPressed", true);
1519 if (!powerButtonMask)
1520 {
1521 sendPowerControlEvent(Event::powerButtonPressed);
1522 setRestartCause(RestartCause::powerButton);
1523 }
1524 else
1525 {
1526 std::cerr << "power button press masked\n";
1527 }
1528 }
1529 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1530 {
1531 powerButtonIface->set_property("ButtonPressed", false);
1532 }
1533 powerButtonEvent.async_wait(
1534 boost::asio::posix::stream_descriptor::wait_read,
1535 [](const boost::system::error_code ec) {
1536 if (ec)
1537 {
1538 std::cerr << "power button handler error: " << ec.message()
1539 << "\n";
1540 return;
1541 }
1542 powerButtonHandler();
1543 });
1544}
1545
1546static void resetButtonHandler()
1547{
1548 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1549
1550 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1551 {
1552 resetButtonPressLog();
1553 resetButtonIface->set_property("ButtonPressed", true);
1554 if (!resetButtonMask)
1555 {
1556 resetInProgress = true;
1557 setRestartCause(RestartCause::resetButton);
1558 }
1559 else
1560 {
1561 std::cerr << "reset button press masked\n";
1562 }
1563 }
1564 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1565 {
1566 resetButtonIface->set_property("ButtonPressed", false);
1567 }
1568 resetButtonEvent.async_wait(
1569 boost::asio::posix::stream_descriptor::wait_read,
1570 [](const boost::system::error_code ec) {
1571 if (ec)
1572 {
1573 std::cerr << "reset button handler error: " << ec.message()
1574 << "\n";
1575 return;
1576 }
1577 resetButtonHandler();
1578 });
1579}
1580
1581static void nmiSetEnablePorperty(bool value)
1582{
1583 conn->async_method_call(
1584 [](boost::system::error_code ec) {
1585 if (ec)
1586 {
1587 std::cerr << "failed to set NMI source\n";
1588 }
1589 },
1590 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1591 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1592 "Enabled", std::variant<bool>{value});
1593}
1594
1595static void nmiReset(void)
1596{
1597 static constexpr const uint8_t value = 1;
1598 const static constexpr int nmiOutPulseTimeMs = 200;
1599
1600 std::cerr << "NMI out action \n";
1601 nmiOutLine.set_value(value);
1602 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1603 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1604 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1605 // restore the NMI_OUT GPIO line back to the opposite value
1606 nmiOutLine.set_value(!value);
1607 std::cerr << nmiOutName << " released\n";
1608 if (ec)
1609 {
1610 // operation_aborted is expected if timer is canceled before
1611 // completion.
1612 if (ec != boost::asio::error::operation_aborted)
1613 {
1614 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1615 << "\n";
1616 }
1617 }
1618 });
1619 // log to redfish
1620 nmiDiagIntLog();
1621 std::cerr << "NMI out action completed\n";
1622 // reset Enable Property
1623 nmiSetEnablePorperty(false);
1624}
1625
1626static void nmiSourcePropertyMonitor(void)
1627{
1628 std::cerr << " NMI Source Property Monitor \n";
1629
1630 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1631 std::make_unique<sdbusplus::bus::match::match>(
1632 *conn,
1633 "type='signal',interface='org.freedesktop.DBus.Properties',"
1634 "member='PropertiesChanged',arg0namespace='com.intel.Control."
1635 "NMISource'",
1636 [](sdbusplus::message::message& msg) {
1637 std::string interfaceName;
1638 boost::container::flat_map<std::string,
1639 std::variant<bool, std::string>>
1640 propertiesChanged;
1641 std::string state;
1642 bool value = true;
1643 try
1644 {
1645 msg.read(interfaceName, propertiesChanged);
1646 if (propertiesChanged.begin()->first == "Enabled")
1647 {
1648 value =
1649 std::get<bool>(propertiesChanged.begin()->second);
1650 std::cerr
1651 << " NMI Enabled propertiesChanged value: " << value
1652 << "\n";
1653 nmiEnabled = value;
1654 if (nmiEnabled)
1655 {
1656 nmiReset();
1657 }
1658 }
1659 }
1660 catch (std::exception& e)
1661 {
1662 std::cerr << "Unable to read NMI source\n";
1663 return;
1664 }
1665 });
1666}
1667
1668static void setNmiSource()
1669{
1670 conn->async_method_call(
1671 [](boost::system::error_code ec) {
1672 if (ec)
1673 {
1674 std::cerr << "failed to set NMI source\n";
1675 }
1676 },
1677 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1678 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1679 "BMCSource",
1680 std::variant<std::string>{
1681 "com.intel.Control.NMISource.BMCSourceSignal.FpBtn"});
1682 // set Enable Property
1683 nmiSetEnablePorperty(true);
1684}
1685
1686static void nmiButtonHandler()
1687{
1688 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1689
1690 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1691 {
1692 nmiButtonPressLog();
1693 nmiButtonIface->set_property("ButtonPressed", true);
1694 if (nmiButtonMasked)
1695 {
1696 std::cerr << "NMI button press masked\n";
1697 }
1698 else
1699 {
1700 setNmiSource();
1701 }
1702 }
1703 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1704 {
1705 nmiButtonIface->set_property("ButtonPressed", false);
1706 }
1707 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1708 [](const boost::system::error_code ec) {
1709 if (ec)
1710 {
1711 std::cerr << "NMI button handler error: "
1712 << ec.message() << "\n";
1713 return;
1714 }
1715 nmiButtonHandler();
1716 });
1717}
1718
1719static void idButtonHandler()
1720{
1721 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1722
1723 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1724 {
1725 idButtonIface->set_property("ButtonPressed", true);
1726 }
1727 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1728 {
1729 idButtonIface->set_property("ButtonPressed", false);
1730 }
1731 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1732 [](const boost::system::error_code& ec) {
1733 if (ec)
1734 {
1735 std::cerr << "ID button handler error: "
1736 << ec.message() << "\n";
1737 return;
1738 }
1739 idButtonHandler();
1740 });
1741}
1742
1743static void postCompleteHandler()
1744{
1745 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1746
1747 bool postComplete =
1748 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
1749 std::cerr << "POST complete value changed: " << postComplete << "\n";
1750 if (postComplete)
1751 {
1752 osIface->set_property("OperatingSystemState", std::string("Standby"));
1753 resetInProgress = false;
1754 }
1755 else
1756 {
1757 osIface->set_property("OperatingSystemState", std::string("Inactive"));
1758 // Set the restart cause if POST complete de-asserted by host software
1759 if (powerState == PowerState::on && !resetInProgress)
1760 {
1761 resetInProgress = true;
1762 setRestartCause(RestartCause::softReset);
1763 }
1764 }
1765 postCompleteEvent.async_wait(
1766 boost::asio::posix::stream_descriptor::wait_read,
1767 [](const boost::system::error_code ec) {
1768 if (ec)
1769 {
1770 std::cerr << "POST complete handler error: " << ec.message()
1771 << "\n";
1772 return;
1773 }
1774 postCompleteHandler();
1775 });
1776}
1777} // namespace power_control
1778
1779int main(int argc, char* argv[])
1780{
1781 std::cerr << "Start Chassis power control service...\n";
1782 power_control::conn =
1783 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1784
1785 // Request all the dbus names
1786 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1787 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
1788 power_control::conn->request_name(
1789 "xyz.openbmc_project.State.OperatingSystem");
1790 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
1791
1792 // Request PS_PWROK GPIO events
1793 if (!power_control::requestGPIOEvents(
1794 "PS_PWROK", power_control::psPowerOKHandler,
1795 power_control::psPowerOKLine, power_control::psPowerOKEvent))
1796 {
1797 return -1;
1798 }
1799
1800 // Request SIO_POWER_GOOD GPIO events
1801 if (!power_control::requestGPIOEvents(
1802 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
1803 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
1804 {
1805 return -1;
1806 }
1807
1808 // Request SIO_ONCONTROL GPIO events
1809 if (!power_control::requestGPIOEvents(
1810 "SIO_ONCONTROL", power_control::sioOnControlHandler,
1811 power_control::sioOnControlLine, power_control::sioOnControlEvent))
1812 {
1813 return -1;
1814 }
1815
1816 // Request SIO_S5 GPIO events
1817 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
1818 power_control::sioS5Line,
1819 power_control::sioS5Event))
1820 {
1821 return -1;
1822 }
1823
1824 // Request POWER_BUTTON GPIO events
1825 if (!power_control::requestGPIOEvents(
1826 "POWER_BUTTON", power_control::powerButtonHandler,
1827 power_control::powerButtonLine, power_control::powerButtonEvent))
1828 {
1829 return -1;
1830 }
1831
1832 // Request RESET_BUTTON GPIO events
1833 if (!power_control::requestGPIOEvents(
1834 "RESET_BUTTON", power_control::resetButtonHandler,
1835 power_control::resetButtonLine, power_control::resetButtonEvent))
1836 {
1837 return -1;
1838 }
1839
1840 // Request NMI_BUTTON GPIO events
1841 if (!power_control::requestGPIOEvents(
1842 "NMI_BUTTON", power_control::nmiButtonHandler,
1843 power_control::nmiButtonLine, power_control::nmiButtonEvent))
1844 {
1845 return -1;
1846 }
1847
1848 // Request ID_BUTTON GPIO events
1849 if (!power_control::requestGPIOEvents(
1850 "ID_BUTTON", power_control::idButtonHandler,
1851 power_control::idButtonLine, power_control::idButtonEvent))
1852 {
1853 return -1;
1854 }
1855
1856 // Request POST_COMPLETE GPIO events
1857 if (!power_control::requestGPIOEvents(
1858 "POST_COMPLETE", power_control::postCompleteHandler,
1859 power_control::postCompleteLine, power_control::postCompleteEvent))
1860 {
1861 return -1;
1862 }
1863
1864 // initialize NMI_OUT GPIO.
1865 if (!power_control::setGPIOOutput(power_control::nmiOutName, 0,
1866 power_control::nmiOutLine))
1867 {
1868 return -1;
1869 }
1870
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001871 // Initialize POWER_OUT and RESET_OUT GPIO.
1872 gpiod::line line;
1873 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
1874 {
1875 return -1;
1876 }
1877
1878 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
1879 {
1880 return -1;
1881 }
1882
1883 // Release line
1884 line.reset();
1885
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001886 // Initialize the power state
1887 power_control::powerState = power_control::PowerState::off;
1888 // Check power good
1889 if (power_control::psPowerOKLine.get_value() > 0)
1890 {
1891 power_control::powerState = power_control::PowerState::on;
1892 }
1893
1894 // Initialize the power state storage
1895 if (power_control::initializePowerStateStorage() < 0)
1896 {
1897 return -1;
1898 }
1899
1900 // Check if we need to start the Power Restore policy
1901 power_control::powerRestorePolicyCheck();
1902
1903 power_control::nmiSourcePropertyMonitor();
1904
1905 std::cerr << "Initializing power state. ";
1906 power_control::logStateTransition(power_control::powerState);
1907
1908 // Power Control Service
1909 sdbusplus::asio::object_server hostServer =
1910 sdbusplus::asio::object_server(power_control::conn);
1911
1912 // Power Control Interface
1913 power_control::hostIface = hostServer.add_interface(
1914 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
1915
1916 power_control::hostIface->register_property(
1917 "RequestedHostTransition",
1918 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
1919 [](const std::string& requested, std::string& resp) {
1920 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
1921 {
1922 sendPowerControlEvent(
1923 power_control::Event::gracefulPowerOffRequest);
1924 }
1925 else if (requested ==
1926 "xyz.openbmc_project.State.Host.Transition.On")
1927 {
1928 sendPowerControlEvent(power_control::Event::powerOnRequest);
1929 setRestartCause(power_control::RestartCause::command);
1930 }
1931 else if (requested ==
1932 "xyz.openbmc_project.State.Host.Transition.Reboot")
1933 {
1934 sendPowerControlEvent(
1935 power_control::Event::gracefulPowerCycleRequest);
1936 setRestartCause(power_control::RestartCause::command);
1937 }
1938 else
1939 {
1940 std::cerr << "Unrecognized host state transition request.\n";
1941 throw std::invalid_argument("Unrecognized Transition Request");
1942 return 0;
1943 }
1944 resp = requested;
1945 return 1;
1946 });
1947 power_control::hostIface->register_property(
1948 "CurrentHostState",
1949 std::string(power_control::getHostState(power_control::powerState)));
1950
1951 power_control::currentHostStateMonitor();
1952
1953 power_control::hostIface->initialize();
1954
1955 // Chassis Control Service
1956 sdbusplus::asio::object_server chassisServer =
1957 sdbusplus::asio::object_server(power_control::conn);
1958
1959 // Chassis Control Interface
1960 power_control::chassisIface =
1961 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
1962 "xyz.openbmc_project.State.Chassis");
1963
1964 power_control::chassisIface->register_property(
1965 "RequestedPowerTransition",
1966 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
1967 [](const std::string& requested, std::string& resp) {
1968 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
1969 {
1970 sendPowerControlEvent(power_control::Event::powerOffRequest);
1971 }
1972 else if (requested ==
1973 "xyz.openbmc_project.State.Chassis.Transition.On")
1974 {
1975 sendPowerControlEvent(power_control::Event::powerOnRequest);
1976 setRestartCause(power_control::RestartCause::command);
1977 }
1978 else if (requested ==
1979 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
1980 {
1981 sendPowerControlEvent(power_control::Event::powerCycleRequest);
1982 setRestartCause(power_control::RestartCause::command);
1983 }
1984 else if (requested ==
1985 "xyz.openbmc_project.State.Chassis.Transition.Reset")
1986 {
1987 setRestartCause(power_control::RestartCause::command);
1988 sendPowerControlEvent(power_control::Event::resetRequest);
1989 }
1990 else
1991 {
1992 std::cerr << "Unrecognized chassis state transition request.\n";
1993 throw std::invalid_argument("Unrecognized Transition Request");
1994 return 0;
1995 }
1996 resp = requested;
1997 return 1;
1998 });
1999 power_control::chassisIface->register_property(
2000 "CurrentPowerState",
2001 std::string(power_control::getChassisState(power_control::powerState)));
2002 power_control::chassisIface->register_property(
2003 "LastStateChangeTime", power_control::getCurrentTimeMs());
2004
2005 power_control::chassisIface->initialize();
2006
2007 // Buttons Service
2008 sdbusplus::asio::object_server buttonsServer =
2009 sdbusplus::asio::object_server(power_control::conn);
2010
2011 // Power Button Interface
2012 power_control::powerButtonIface = buttonsServer.add_interface(
2013 "/xyz/openbmc_project/chassis/buttons/power",
2014 "xyz.openbmc_project.Chassis.Buttons");
2015
2016 power_control::powerButtonIface->register_property(
2017 "ButtonMasked", false, [](const bool requested, bool& current) {
2018 if (requested)
2019 {
2020 if (power_control::powerButtonMask)
2021 {
2022 return 1;
2023 }
2024 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002025 power_control::powerOutName, 1,
2026 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002027 {
2028 throw std::runtime_error("Failed to request GPIO");
2029 return 0;
2030 }
2031 std::cerr << "Power Button Masked.\n";
2032 }
2033 else
2034 {
2035 if (!power_control::powerButtonMask)
2036 {
2037 return 1;
2038 }
2039 std::cerr << "Power Button Un-masked\n";
2040 power_control::powerButtonMask.reset();
2041 }
2042 // Update the mask setting
2043 current = requested;
2044 return 1;
2045 });
2046
2047 // Check power button state
2048 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2049 power_control::powerButtonIface->register_property("ButtonPressed",
2050 powerButtonPressed);
2051
2052 power_control::powerButtonIface->initialize();
2053
2054 // Reset Button Interface
2055 power_control::resetButtonIface = buttonsServer.add_interface(
2056 "/xyz/openbmc_project/chassis/buttons/reset",
2057 "xyz.openbmc_project.Chassis.Buttons");
2058
2059 power_control::resetButtonIface->register_property(
2060 "ButtonMasked", false, [](const bool requested, bool& current) {
2061 if (requested)
2062 {
2063 if (power_control::resetButtonMask)
2064 {
2065 return 1;
2066 }
2067 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002068 power_control::resetOutName, 1,
2069 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002070 {
2071 throw std::runtime_error("Failed to request GPIO");
2072 return 0;
2073 }
2074 std::cerr << "Reset Button Masked.\n";
2075 }
2076 else
2077 {
2078 if (!power_control::resetButtonMask)
2079 {
2080 return 1;
2081 }
2082 std::cerr << "Reset Button Un-masked\n";
2083 power_control::resetButtonMask.reset();
2084 }
2085 // Update the mask setting
2086 current = requested;
2087 return 1;
2088 });
2089
2090 // Check reset button state
2091 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2092 power_control::resetButtonIface->register_property("ButtonPressed",
2093 resetButtonPressed);
2094
2095 power_control::resetButtonIface->initialize();
2096
2097 // NMI Button Interface
2098 power_control::nmiButtonIface =
2099 buttonsServer.add_interface("/xyz/openbmc_project/chassis/buttons/nmi",
2100 "xyz.openbmc_project.Chassis.Buttons");
2101
2102 power_control::nmiButtonIface->register_property(
2103 "ButtonMasked", false, [](const bool requested, bool& current) {
2104 if (power_control::nmiButtonMasked == requested)
2105 {
2106 // NMI button mask is already set as requested, so no change
2107 return 1;
2108 }
2109 if (requested)
2110 {
2111 std::cerr << "NMI Button Masked.\n";
2112 power_control::nmiButtonMasked = true;
2113 }
2114 else
2115 {
2116 std::cerr << "NMI Button Un-masked.\n";
2117 power_control::nmiButtonMasked = false;
2118 }
2119 // Update the mask setting
2120 current = power_control::nmiButtonMasked;
2121 return 1;
2122 });
2123
2124 // Check NMI button state
2125 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2126 power_control::nmiButtonIface->register_property("ButtonPressed",
2127 nmiButtonPressed);
2128
2129 power_control::nmiButtonIface->initialize();
2130
2131 // ID Button Interface
2132 power_control::idButtonIface =
2133 buttonsServer.add_interface("/xyz/openbmc_project/chassis/buttons/id",
2134 "xyz.openbmc_project.Chassis.Buttons");
2135
2136 // Check ID button state
2137 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2138 power_control::idButtonIface->register_property("ButtonPressed",
2139 idButtonPressed);
2140
2141 power_control::idButtonIface->initialize();
2142
2143 // OS State Service
2144 sdbusplus::asio::object_server osServer =
2145 sdbusplus::asio::object_server(power_control::conn);
2146
2147 // OS State Interface
2148 power_control::osIface = osServer.add_interface(
2149 "/xyz/openbmc_project/state/os",
2150 "xyz.openbmc_project.State.OperatingSystem.Status");
2151
2152 // Get the initial OS state based on POST complete
2153 // 0: Asserted, OS state is "Standby" (ready to boot)
2154 // 1: De-Asserted, OS state is "Inactive"
2155 std::string osState = power_control::postCompleteLine.get_value() > 0
2156 ? "Inactive"
2157 : "Standby";
2158
2159 power_control::osIface->register_property("OperatingSystemState",
2160 std::string(osState));
2161
2162 power_control::osIface->initialize();
2163
2164 power_control::io.run();
2165
2166 return 0;
2167}