blob: 07f6a308cc1976e23396d1447d7136d927cc2944 [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
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700480static void systemPowerGoodFailedLog()
481{
482 sd_journal_send(
483 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
484 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
485 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
486 sioPowerGoodWatchdogTimeMs, NULL);
487}
488
489static void psPowerOKFailedLog()
490{
491 sd_journal_send(
492 "MESSAGE=PowerControl: power supply power good failed to assert",
493 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
494 "OpenBMC.0.1.PowerSupplyPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
495 psPowerOKWatchdogTimeMs, NULL);
496}
497
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700498static void powerRestorePolicyLog()
499{
500 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
501 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
502 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
503}
504
505static void powerButtonPressLog()
506{
507 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
508 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
509 "OpenBMC.0.1.PowerButtonPressed", NULL);
510}
511
512static void resetButtonPressLog()
513{
514 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
515 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
516 "OpenBMC.0.1.ResetButtonPressed", NULL);
517}
518
519static void nmiButtonPressLog()
520{
521 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
522 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
523 "OpenBMC.0.1.NMIButtonPressed", NULL);
524}
525
526static void nmiDiagIntLog()
527{
528 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
529 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
530 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
531}
532
533static int initializePowerStateStorage()
534{
535 // create the power control directory if it doesn't exist
536 std::error_code ec;
537 if (!(std::filesystem::create_directories(powerControlDir, ec)))
538 {
539 if (ec.value() != 0)
540 {
541 std::cerr << "failed to create " << powerControlDir << ": "
542 << ec.message() << "\n";
543 return -1;
544 }
545 }
546 // Create the power state file if it doesn't exist
547 if (!std::filesystem::exists(powerControlDir / powerStateFile))
548 {
549 std::ofstream powerStateStream(powerControlDir / powerStateFile);
550 powerStateStream << getChassisState(powerState);
551 }
552 return 0;
553}
554
555static bool wasPowerDropped()
556{
557 std::ifstream powerStateStream(powerControlDir / powerStateFile);
558 if (!powerStateStream.is_open())
559 {
560 std::cerr << "Failed to open power state file\n";
561 return false;
562 }
563
564 std::string state;
565 std::getline(powerStateStream, state);
566 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
567}
568
569static void invokePowerRestorePolicy(const std::string& policy)
570{
571 // Async events may call this twice, but we only want to run once
572 static bool policyInvoked = false;
573 if (policyInvoked)
574 {
575 return;
576 }
577 policyInvoked = true;
578
579 std::cerr << "Power restore delay expired, invoking " << policy << "\n";
580 if (policy ==
581 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
582 {
583 sendPowerControlEvent(Event::powerOnRequest);
584 setRestartCause(RestartCause::powerPolicyOn);
585 }
586 else if (policy == "xyz.openbmc_project.Control.Power.RestorePolicy."
587 "Policy.Restore")
588 {
589 if (wasPowerDropped())
590 {
591 std::cerr << "Power was dropped, restoring Host On state\n";
592 sendPowerControlEvent(Event::powerOnRequest);
593 setRestartCause(RestartCause::powerPolicyRestore);
594 }
595 else
596 {
597 std::cerr << "No power drop, restoring Host Off state\n";
598 }
599 }
600}
601
602static void powerRestorePolicyDelay(int delay)
603{
604 // Async events may call this twice, but we only want to run once
605 static bool delayStarted = false;
606 if (delayStarted)
607 {
608 return;
609 }
610 delayStarted = true;
611 // Calculate the delay from now to meet the requested delay
612 // Subtract the approximate uboot time
613 static constexpr const int ubootSeconds = 20;
614 delay -= ubootSeconds;
615 // Subtract the time since boot
616 struct sysinfo info = {};
617 if (sysinfo(&info) == 0)
618 {
619 delay -= info.uptime;
620 }
621 // 0 is the minimum delay
622 delay = std::max(delay, 0);
623
624 static boost::asio::steady_timer powerRestorePolicyTimer(io);
625 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
626 std::cerr << "Power restore delay of " << delay << " seconds started\n";
627 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
628 if (ec)
629 {
630 // operation_aborted is expected if timer is canceled before
631 // completion.
632 if (ec != boost::asio::error::operation_aborted)
633 {
634 std::cerr << "power restore policy async_wait failed: "
635 << ec.message() << "\n";
636 }
637 return;
638 }
639 // Get Power Restore Policy
640 // In case PowerRestorePolicy is not available, set a match for it
641 static std::unique_ptr<sdbusplus::bus::match::match>
642 powerRestorePolicyMatch = std::make_unique<
643 sdbusplus::bus::match::match>(
644 *conn,
645 "type='signal',interface='org.freedesktop.DBus.Properties',"
646 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
647 "project.Control.Power.RestorePolicy'",
648 [](sdbusplus::message::message& msg) {
649 std::string interfaceName;
650 boost::container::flat_map<std::string,
651 std::variant<std::string>>
652 propertiesChanged;
653 std::string policy;
654 try
655 {
656 msg.read(interfaceName, propertiesChanged);
657 policy = std::get<std::string>(
658 propertiesChanged.begin()->second);
659 }
660 catch (std::exception& e)
661 {
662 std::cerr
663 << "Unable to read power restore policy value\n";
664 powerRestorePolicyMatch.reset();
665 return;
666 }
667 invokePowerRestorePolicy(policy);
668 powerRestorePolicyMatch.reset();
669 });
670
671 // Check if it's already on DBus
672 conn->async_method_call(
673 [](boost::system::error_code ec,
674 const std::variant<std::string>& policyProperty) {
675 if (ec)
676 {
677 return;
678 }
679 powerRestorePolicyMatch.reset();
680 const std::string* policy =
681 std::get_if<std::string>(&policyProperty);
682 if (policy == nullptr)
683 {
684 std::cerr << "Unable to read power restore policy value\n";
685 return;
686 }
687 invokePowerRestorePolicy(*policy);
688 },
689 "xyz.openbmc_project.Settings",
690 "/xyz/openbmc_project/control/host0/power_restore_policy",
691 "org.freedesktop.DBus.Properties", "Get",
692 "xyz.openbmc_project.Control.Power.RestorePolicy",
693 "PowerRestorePolicy");
694 });
695}
696
697static void powerRestorePolicyStart()
698{
699 std::cerr << "Power restore policy started\n";
700 powerRestorePolicyLog();
701
702 // Get the desired delay time
703 // In case PowerRestoreDelay is not available, set a match for it
704 static std::unique_ptr<sdbusplus::bus::match::match>
705 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
706 *conn,
707 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
708 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
709 "Power.RestoreDelay'",
710 [](sdbusplus::message::message& msg) {
711 std::string interfaceName;
712 boost::container::flat_map<std::string, std::variant<uint16_t>>
713 propertiesChanged;
714 int delay = 0;
715 try
716 {
717 msg.read(interfaceName, propertiesChanged);
718 delay =
719 std::get<uint16_t>(propertiesChanged.begin()->second);
720 }
721 catch (std::exception& e)
722 {
723 std::cerr << "Unable to read power restore delay value\n";
724 powerRestoreDelayMatch.reset();
725 return;
726 }
727 powerRestorePolicyDelay(delay);
728 powerRestoreDelayMatch.reset();
729 });
730
731 // Check if it's already on DBus
732 conn->async_method_call(
733 [](boost::system::error_code ec,
734 const std::variant<uint16_t>& delayProperty) {
735 if (ec)
736 {
737 return;
738 }
739 powerRestoreDelayMatch.reset();
740 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
741 if (delay == nullptr)
742 {
743 std::cerr << "Unable to read power restore delay value\n";
744 return;
745 }
746 powerRestorePolicyDelay(*delay);
747 },
748 "xyz.openbmc_project.Settings",
749 "/xyz/openbmc_project/control/power_restore_delay",
750 "org.freedesktop.DBus.Properties", "Get",
751 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
752}
753
754static void powerRestorePolicyCheck()
755{
756 // In case ACBoot is not available, set a match for it
757 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
758 std::make_unique<sdbusplus::bus::match::match>(
759 *conn,
760 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
761 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
762 "ACBoot'",
763 [](sdbusplus::message::message& msg) {
764 std::string interfaceName;
765 boost::container::flat_map<std::string,
766 std::variant<std::string>>
767 propertiesChanged;
768 std::string acBoot;
769 try
770 {
771 msg.read(interfaceName, propertiesChanged);
772 acBoot = std::get<std::string>(
773 propertiesChanged.begin()->second);
774 }
775 catch (std::exception& e)
776 {
777 std::cerr << "Unable to read AC Boot status\n";
778 acBootMatch.reset();
779 return;
780 }
781 if (acBoot == "Unknown")
782 {
783 return;
784 }
785 if (acBoot == "True")
786 {
787 // Start the Power Restore policy
788 powerRestorePolicyStart();
789 }
790 acBootMatch.reset();
791 });
792
793 // Check if it's already on DBus
794 conn->async_method_call(
795 [](boost::system::error_code ec,
796 const std::variant<std::string>& acBootProperty) {
797 if (ec)
798 {
799 return;
800 }
801 const std::string* acBoot =
802 std::get_if<std::string>(&acBootProperty);
803 if (acBoot == nullptr)
804 {
805 std::cerr << "Unable to read AC Boot status\n";
806 return;
807 }
808 if (*acBoot == "Unknown")
809 {
810 return;
811 }
812 if (*acBoot == "True")
813 {
814 // Start the Power Restore policy
815 powerRestorePolicyStart();
816 }
817 acBootMatch.reset();
818 },
819 "xyz.openbmc_project.Settings",
820 "/xyz/openbmc_project/control/host0/ac_boot",
821 "org.freedesktop.DBus.Properties", "Get",
822 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
823}
824
825static bool requestGPIOEvents(
826 const std::string& name, const std::function<void()>& handler,
827 gpiod::line& gpioLine,
828 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
829{
830 // Find the GPIO line
831 gpioLine = gpiod::find_line(name);
832 if (!gpioLine)
833 {
834 std::cerr << "Failed to find the " << name << " line\n";
835 return false;
836 }
837
838 try
839 {
840 gpioLine.request(
841 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
842 }
843 catch (std::exception&)
844 {
845 std::cerr << "Failed to request events for " << name << "\n";
846 return false;
847 }
848
849 int gpioLineFd = gpioLine.event_get_fd();
850 if (gpioLineFd < 0)
851 {
852 std::cerr << "Failed to get " << name << " fd\n";
853 return false;
854 }
855
856 gpioEventDescriptor.assign(gpioLineFd);
857
858 gpioEventDescriptor.async_wait(
859 boost::asio::posix::stream_descriptor::wait_read,
860 [&name, handler](const boost::system::error_code ec) {
861 if (ec)
862 {
863 std::cerr << name << " fd handler error: " << ec.message()
864 << "\n";
865 // TODO: throw here to force power-control to restart?
866 return;
867 }
868 handler();
869 });
870 return true;
871}
872
873static bool setGPIOOutput(const std::string& name, const int value,
874 gpiod::line& gpioLine)
875{
876 // Find the GPIO line
877 gpioLine = gpiod::find_line(name);
878 if (!gpioLine)
879 {
880 std::cerr << "Failed to find the " << name << " line.\n";
881 return false;
882 }
883
884 // Request GPIO output to specified value
885 try
886 {
887 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
888 value);
889 }
890 catch (std::exception&)
891 {
892 std::cerr << "Failed to request " << name << " output\n";
893 return false;
894 }
895
896 std::cerr << name << " set to " << std::to_string(value) << "\n";
897 return true;
898}
899
900static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
901 const std::string& name, const int value,
902 const int durationMs)
903{
904 // Set the masked GPIO line to the specified value
905 maskedGPIOLine.set_value(value);
906 std::cerr << name << " set to " << std::to_string(value) << "\n";
907 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
908 gpioAssertTimer.async_wait(
909 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
910 // Set the masked GPIO line back to the opposite value
911 maskedGPIOLine.set_value(!value);
912 std::cerr << name << " released\n";
913 if (ec)
914 {
915 // operation_aborted is expected if timer is canceled before
916 // completion.
917 if (ec != boost::asio::error::operation_aborted)
918 {
919 std::cerr << name << " async_wait failed: " + ec.message()
920 << "\n";
921 }
922 }
923 });
924 return 0;
925}
926
927static int setGPIOOutputForMs(const std::string& name, const int value,
928 const int durationMs)
929{
930 // If the requested GPIO is masked, use the mask line to set the output
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700931 if (powerButtonMask && name == power_control::powerOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700932 {
933 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
934 durationMs);
935 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700936 if (resetButtonMask && name == power_control::resetOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700937 {
938 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
939 durationMs);
940 }
941
942 // No mask set, so request and set the GPIO normally
943 gpiod::line gpioLine;
944 if (!setGPIOOutput(name, value, gpioLine))
945 {
946 return -1;
947 }
948 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
949 gpioAssertTimer.async_wait(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700950 [gpioLine, value, name](const boost::system::error_code ec) {
951 // Set the GPIO line back to the opposite value
952 gpioLine.set_value(!value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700953 std::cerr << name << " released\n";
954 if (ec)
955 {
956 // operation_aborted is expected if timer is canceled before
957 // completion.
958 if (ec != boost::asio::error::operation_aborted)
959 {
960 std::cerr << name << " async_wait failed: " << ec.message()
961 << "\n";
962 }
963 }
964 });
965 return 0;
966}
967
968static void powerOn()
969{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700970 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700971}
972
973static void gracefulPowerOff()
974{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700975 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700976}
977
978static void forcePowerOff()
979{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -0700980 if (setGPIOOutputForMs(power_control::powerOutName, 0,
981 forceOffPulseTimeMs) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700982 {
983 return;
984 }
985
986 // If the force off timer expires, then the PCH power-button override
987 // failed, so attempt the Unconditional Powerdown SMBus command.
988 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
989 if (ec)
990 {
991 // operation_aborted is expected if timer is canceled before
992 // completion.
993 if (ec != boost::asio::error::operation_aborted)
994 {
995 std::cerr << "Force power off async_wait failed: "
996 << ec.message() << "\n";
997 }
998 return;
999 }
1000 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
1001 "Powerdown SMBus command.\n";
1002 const static constexpr size_t pchDevBusAddress = 3;
1003 const static constexpr size_t pchDevSlaveAddress = 0x44;
1004 const static constexpr size_t pchCmdReg = 0;
1005 const static constexpr size_t pchPowerDownCmd = 0x02;
1006 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
1007 pchPowerDownCmd) < 0)
1008 {
1009 std::cerr << "Unconditional Powerdown command failed! Not sure "
1010 "what to do now.\n";
1011 }
1012 });
1013}
1014
1015static void reset()
1016{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001017 setGPIOOutputForMs(power_control::resetOutName, 0, resetPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001018}
1019
1020static void gracefulPowerOffTimerStart()
1021{
1022 std::cerr << "Graceful power-off timer started\n";
1023 gracefulPowerOffTimer.expires_after(
1024 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1025 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1026 if (ec)
1027 {
1028 // operation_aborted is expected if timer is canceled before
1029 // completion.
1030 if (ec != boost::asio::error::operation_aborted)
1031 {
1032 std::cerr << "Graceful power-off async_wait failed: "
1033 << ec.message() << "\n";
1034 }
1035 std::cerr << "Graceful power-off timer canceled\n";
1036 return;
1037 }
1038 std::cerr << "Graceful power-off timer completed\n";
1039 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1040 });
1041}
1042
1043static void powerCycleTimerStart()
1044{
1045 std::cerr << "Power-cycle timer started\n";
1046 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1047 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1048 if (ec)
1049 {
1050 // operation_aborted is expected if timer is canceled before
1051 // completion.
1052 if (ec != boost::asio::error::operation_aborted)
1053 {
1054 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1055 << "\n";
1056 }
1057 std::cerr << "Power-cycle timer canceled\n";
1058 return;
1059 }
1060 std::cerr << "Power-cycle timer completed\n";
1061 sendPowerControlEvent(Event::powerCycleTimerExpired);
1062 });
1063}
1064
1065static void psPowerOKWatchdogTimerStart()
1066{
1067 std::cerr << "power supply power OK watchdog timer started\n";
1068 psPowerOKWatchdogTimer.expires_after(
1069 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1070 psPowerOKWatchdogTimer.async_wait(
1071 [](const boost::system::error_code ec) {
1072 if (ec)
1073 {
1074 // operation_aborted is expected if timer is canceled before
1075 // completion.
1076 if (ec != boost::asio::error::operation_aborted)
1077 {
1078 std::cerr
1079 << "power supply power OK watchdog async_wait failed: "
1080 << ec.message() << "\n";
1081 }
1082 std::cerr << "power supply power OK watchdog timer canceled\n";
1083 return;
1084 }
1085 std::cerr << "power supply power OK watchdog timer expired\n";
1086 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1087 });
1088}
1089
1090static void pohCounterTimerStart()
1091{
1092 std::cerr << "POH timer started\n";
1093 // Set the time-out as 1 hour, to align with POH command in ipmid
1094 pohCounterTimer.expires_after(std::chrono::hours(1));
1095 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1096 if (ec)
1097 {
1098 // operation_aborted is expected if timer is canceled before
1099 // completion.
1100 if (ec != boost::asio::error::operation_aborted)
1101 {
1102 std::cerr << "POH timer async_wait failed: " << ec.message()
1103 << "\n";
1104 }
1105 std::cerr << "POH timer canceled\n";
1106 return;
1107 }
1108
1109 if (getHostState(powerState) !=
1110 "xyz.openbmc_project.State.Host.HostState.Running")
1111 {
1112 return;
1113 }
1114
1115 conn->async_method_call(
1116 [](boost::system::error_code ec,
1117 const std::variant<uint32_t>& pohCounterProperty) {
1118 if (ec)
1119 {
1120 std::cerr << "error to get poh counter\n";
1121 return;
1122 }
1123 const uint32_t* pohCounter =
1124 std::get_if<uint32_t>(&pohCounterProperty);
1125 if (pohCounter == nullptr)
1126 {
1127 std::cerr << "unable to read poh counter\n";
1128 return;
1129 }
1130
1131 conn->async_method_call(
1132 [](boost::system::error_code ec) {
1133 if (ec)
1134 {
1135 std::cerr << "failed to set poh counter\n";
1136 }
1137 },
1138 "xyz.openbmc_project.Settings",
1139 "/xyz/openbmc_project/state/chassis0",
1140 "org.freedesktop.DBus.Properties", "Set",
1141 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1142 std::variant<uint32_t>(*pohCounter + 1));
1143 },
1144 "xyz.openbmc_project.Settings",
1145 "/xyz/openbmc_project/state/chassis0",
1146 "org.freedesktop.DBus.Properties", "Get",
1147 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1148
1149 pohCounterTimerStart();
1150 });
1151}
1152
1153static void currentHostStateMonitor()
1154{
1155 static auto match = sdbusplus::bus::match::match(
1156 *conn,
1157 "type='signal',member='PropertiesChanged', "
1158 "interface='org.freedesktop.DBus.Properties', "
1159 "arg0namespace='xyz.openbmc_project.State.Host'",
1160 [](sdbusplus::message::message& message) {
1161 std::string intfName;
1162 std::map<std::string, std::variant<std::string>> properties;
1163
1164 message.read(intfName, properties);
1165
1166 std::variant<std::string> currentHostState;
1167
1168 try
1169 {
1170 currentHostState = properties.at("CurrentHostState");
1171 }
1172 catch (const std::out_of_range& e)
1173 {
1174 std::cerr << "Error in finding CurrentHostState property\n";
1175
1176 return;
1177 }
1178
1179 if (std::get<std::string>(currentHostState) ==
1180 "xyz.openbmc_project.State.Host.HostState.Running")
1181 {
1182 pohCounterTimerStart();
1183 }
1184 else
1185 {
1186 pohCounterTimer.cancel();
1187 }
1188 });
1189}
1190
1191static void sioPowerGoodWatchdogTimerStart()
1192{
1193 std::cerr << "SIO power good watchdog timer started\n";
1194 sioPowerGoodWatchdogTimer.expires_after(
1195 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1196 sioPowerGoodWatchdogTimer.async_wait(
1197 [](const boost::system::error_code ec) {
1198 if (ec)
1199 {
1200 // operation_aborted is expected if timer is canceled before
1201 // completion.
1202 if (ec != boost::asio::error::operation_aborted)
1203 {
1204 std::cerr << "SIO power good watchdog async_wait failed: "
1205 << ec.message() << "\n";
1206 }
1207 std::cerr << "SIO power good watchdog timer canceled\n";
1208 return;
1209 }
1210 std::cerr << "SIO power good watchdog timer completed\n";
1211 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1212 });
1213}
1214
1215static void powerStateOn(const Event event)
1216{
1217 logEvent(__FUNCTION__, event);
1218 switch (event)
1219 {
1220 case Event::psPowerOKDeAssert:
1221 setPowerState(PowerState::off);
1222 // DC power is unexpectedly lost, beep
1223 beep(beepPowerFail);
1224 break;
1225 case Event::sioS5Assert:
1226 setPowerState(PowerState::transitionToOff);
1227 setRestartCause(RestartCause::softReset);
1228 break;
1229 case Event::powerButtonPressed:
1230 setPowerState(PowerState::gracefulTransitionToOff);
1231 gracefulPowerOffTimerStart();
1232 break;
1233 case Event::powerOffRequest:
1234 setPowerState(PowerState::transitionToOff);
1235 forcePowerOff();
1236 break;
1237 case Event::gracefulPowerOffRequest:
1238 setPowerState(PowerState::gracefulTransitionToOff);
1239 gracefulPowerOffTimerStart();
1240 gracefulPowerOff();
1241 break;
1242 case Event::powerCycleRequest:
1243 setPowerState(PowerState::transitionToCycleOff);
1244 forcePowerOff();
1245 break;
1246 case Event::gracefulPowerCycleRequest:
1247 setPowerState(PowerState::gracefulTransitionToCycleOff);
1248 gracefulPowerOffTimerStart();
1249 gracefulPowerOff();
1250 break;
1251 case Event::resetRequest:
1252 reset();
1253 break;
1254 default:
1255 std::cerr << "No action taken.\n";
1256 break;
1257 }
1258}
1259
1260static void powerStateWaitForPSPowerOK(const Event event)
1261{
1262 logEvent(__FUNCTION__, event);
1263 switch (event)
1264 {
1265 case Event::psPowerOKAssert:
1266 // Cancel any GPIO assertions held during the transition
1267 gpioAssertTimer.cancel();
1268 psPowerOKWatchdogTimer.cancel();
1269 sioPowerGoodWatchdogTimerStart();
1270 setPowerState(PowerState::waitForSIOPowerGood);
1271 break;
1272 case Event::psPowerOKWatchdogTimerExpired:
1273 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001274 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001275 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001276 case Event::sioPowerGoodAssert:
1277 psPowerOKWatchdogTimer.cancel();
1278 setPowerState(PowerState::on);
1279 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001280 default:
1281 std::cerr << "No action taken.\n";
1282 break;
1283 }
1284}
1285
1286static void powerStateWaitForSIOPowerGood(const Event event)
1287{
1288 logEvent(__FUNCTION__, event);
1289 switch (event)
1290 {
1291 case Event::sioPowerGoodAssert:
1292 sioPowerGoodWatchdogTimer.cancel();
1293 setPowerState(PowerState::on);
1294 break;
1295 case Event::sioPowerGoodWatchdogTimerExpired:
1296 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001297 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001298 forcePowerOff();
1299 break;
1300 default:
1301 std::cerr << "No action taken.\n";
1302 break;
1303 }
1304}
1305
1306static void powerStateFailedTransitionToOn(const Event event)
1307{
1308 logEvent(__FUNCTION__, event);
1309 switch (event)
1310 {
1311 case Event::psPowerOKAssert:
1312 // We're in a failure state, so don't allow the system to turn on
1313 // without a user request
1314 forcePowerOff();
1315 break;
1316 case Event::psPowerOKDeAssert:
1317 // Cancel any GPIO assertions held during the transition
1318 gpioAssertTimer.cancel();
1319 break;
1320 case Event::powerButtonPressed:
1321 psPowerOKWatchdogTimerStart();
1322 setPowerState(PowerState::waitForPSPowerOK);
1323 break;
1324 case Event::powerOnRequest:
1325 psPowerOKWatchdogTimerStart();
1326 setPowerState(PowerState::waitForPSPowerOK);
1327 powerOn();
1328 break;
1329 default:
1330 std::cerr << "No action taken.\n";
1331 break;
1332 }
1333}
1334
1335static void powerStateOff(const Event event)
1336{
1337 logEvent(__FUNCTION__, event);
1338 switch (event)
1339 {
1340 case Event::psPowerOKAssert:
1341 setPowerState(PowerState::waitForSIOPowerGood);
1342 break;
1343 case Event::sioS5DeAssert:
1344 setPowerState(PowerState::waitForPSPowerOK);
1345 break;
1346 case Event::powerButtonPressed:
1347 psPowerOKWatchdogTimerStart();
1348 setPowerState(PowerState::waitForPSPowerOK);
1349 break;
1350 case Event::powerOnRequest:
1351 psPowerOKWatchdogTimerStart();
1352 setPowerState(PowerState::waitForPSPowerOK);
1353 powerOn();
1354 break;
1355 default:
1356 std::cerr << "No action taken.\n";
1357 break;
1358 }
1359}
1360
1361static void powerStateTransitionToOff(const Event event)
1362{
1363 logEvent(__FUNCTION__, event);
1364 switch (event)
1365 {
1366 case Event::psPowerOKDeAssert:
1367 // Cancel any GPIO assertions held during the transition
1368 gpioAssertTimer.cancel();
1369 setPowerState(PowerState::off);
1370 break;
1371 default:
1372 std::cerr << "No action taken.\n";
1373 break;
1374 }
1375}
1376
1377static void powerStateGracefulTransitionToOff(const Event event)
1378{
1379 logEvent(__FUNCTION__, event);
1380 switch (event)
1381 {
1382 case Event::psPowerOKDeAssert:
1383 gracefulPowerOffTimer.cancel();
1384 setPowerState(PowerState::off);
1385 break;
1386 case Event::gracefulPowerOffTimerExpired:
1387 setPowerState(PowerState::on);
1388 break;
1389 default:
1390 std::cerr << "No action taken.\n";
1391 break;
1392 }
1393}
1394
1395static void powerStateCycleOff(const Event event)
1396{
1397 logEvent(__FUNCTION__, event);
1398 switch (event)
1399 {
1400 case Event::powerCycleTimerExpired:
1401 psPowerOKWatchdogTimerStart();
1402 setPowerState(PowerState::waitForPSPowerOK);
1403 powerOn();
1404 break;
1405 default:
1406 std::cerr << "No action taken.\n";
1407 break;
1408 }
1409}
1410
1411static void powerStateTransitionToCycleOff(const Event event)
1412{
1413 logEvent(__FUNCTION__, event);
1414 switch (event)
1415 {
1416 case Event::psPowerOKDeAssert:
1417 // Cancel any GPIO assertions held during the transition
1418 gpioAssertTimer.cancel();
1419 setPowerState(PowerState::cycleOff);
1420 powerCycleTimerStart();
1421 break;
1422 default:
1423 std::cerr << "No action taken.\n";
1424 break;
1425 }
1426}
1427
1428static void powerStateGracefulTransitionToCycleOff(const Event event)
1429{
1430 logEvent(__FUNCTION__, event);
1431 switch (event)
1432 {
1433 case Event::psPowerOKDeAssert:
1434 gracefulPowerOffTimer.cancel();
1435 setPowerState(PowerState::cycleOff);
1436 powerCycleTimerStart();
1437 break;
1438 case Event::gracefulPowerOffTimerExpired:
1439 setPowerState(PowerState::on);
1440 break;
1441 default:
1442 std::cerr << "No action taken.\n";
1443 break;
1444 }
1445}
1446
1447static void psPowerOKHandler()
1448{
1449 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1450
1451 Event powerControlEvent =
1452 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1453 ? Event::psPowerOKAssert
1454 : Event::psPowerOKDeAssert;
1455
1456 sendPowerControlEvent(powerControlEvent);
1457 psPowerOKEvent.async_wait(
1458 boost::asio::posix::stream_descriptor::wait_read,
1459 [](const boost::system::error_code ec) {
1460 if (ec)
1461 {
1462 std::cerr << "power supply power OK handler error: "
1463 << ec.message() << "\n";
1464 return;
1465 }
1466 psPowerOKHandler();
1467 });
1468}
1469
1470static void sioPowerGoodHandler()
1471{
1472 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1473
1474 Event powerControlEvent =
1475 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1476 ? Event::sioPowerGoodAssert
1477 : Event::sioPowerGoodDeAssert;
1478
1479 sendPowerControlEvent(powerControlEvent);
1480 sioPowerGoodEvent.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 power good handler error: " << ec.message()
1486 << "\n";
1487 return;
1488 }
1489 sioPowerGoodHandler();
1490 });
1491}
1492
1493static void sioOnControlHandler()
1494{
1495 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1496
1497 bool sioOnControl =
1498 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1499 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1500 sioOnControlEvent.async_wait(
1501 boost::asio::posix::stream_descriptor::wait_read,
1502 [](const boost::system::error_code ec) {
1503 if (ec)
1504 {
1505 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1506 << "\n";
1507 return;
1508 }
1509 sioOnControlHandler();
1510 });
1511}
1512
1513static void sioS5Handler()
1514{
1515 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1516
1517 Event powerControlEvent =
1518 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1519 ? Event::sioS5Assert
1520 : Event::sioS5DeAssert;
1521
1522 sendPowerControlEvent(powerControlEvent);
1523 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1524 [](const boost::system::error_code ec) {
1525 if (ec)
1526 {
1527 std::cerr << "SIO S5 handler error: "
1528 << ec.message() << "\n";
1529 return;
1530 }
1531 sioS5Handler();
1532 });
1533}
1534
1535static void powerButtonHandler()
1536{
1537 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1538
1539 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1540 {
1541 powerButtonPressLog();
1542 powerButtonIface->set_property("ButtonPressed", true);
1543 if (!powerButtonMask)
1544 {
1545 sendPowerControlEvent(Event::powerButtonPressed);
1546 setRestartCause(RestartCause::powerButton);
1547 }
1548 else
1549 {
1550 std::cerr << "power button press masked\n";
1551 }
1552 }
1553 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1554 {
1555 powerButtonIface->set_property("ButtonPressed", false);
1556 }
1557 powerButtonEvent.async_wait(
1558 boost::asio::posix::stream_descriptor::wait_read,
1559 [](const boost::system::error_code ec) {
1560 if (ec)
1561 {
1562 std::cerr << "power button handler error: " << ec.message()
1563 << "\n";
1564 return;
1565 }
1566 powerButtonHandler();
1567 });
1568}
1569
1570static void resetButtonHandler()
1571{
1572 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1573
1574 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1575 {
1576 resetButtonPressLog();
1577 resetButtonIface->set_property("ButtonPressed", true);
1578 if (!resetButtonMask)
1579 {
1580 resetInProgress = true;
1581 setRestartCause(RestartCause::resetButton);
1582 }
1583 else
1584 {
1585 std::cerr << "reset button press masked\n";
1586 }
1587 }
1588 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1589 {
1590 resetButtonIface->set_property("ButtonPressed", false);
1591 }
1592 resetButtonEvent.async_wait(
1593 boost::asio::posix::stream_descriptor::wait_read,
1594 [](const boost::system::error_code ec) {
1595 if (ec)
1596 {
1597 std::cerr << "reset button handler error: " << ec.message()
1598 << "\n";
1599 return;
1600 }
1601 resetButtonHandler();
1602 });
1603}
1604
1605static void nmiSetEnablePorperty(bool value)
1606{
1607 conn->async_method_call(
1608 [](boost::system::error_code ec) {
1609 if (ec)
1610 {
1611 std::cerr << "failed to set NMI source\n";
1612 }
1613 },
1614 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1615 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1616 "Enabled", std::variant<bool>{value});
1617}
1618
1619static void nmiReset(void)
1620{
1621 static constexpr const uint8_t value = 1;
1622 const static constexpr int nmiOutPulseTimeMs = 200;
1623
1624 std::cerr << "NMI out action \n";
1625 nmiOutLine.set_value(value);
1626 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1627 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1628 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1629 // restore the NMI_OUT GPIO line back to the opposite value
1630 nmiOutLine.set_value(!value);
1631 std::cerr << nmiOutName << " released\n";
1632 if (ec)
1633 {
1634 // operation_aborted is expected if timer is canceled before
1635 // completion.
1636 if (ec != boost::asio::error::operation_aborted)
1637 {
1638 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1639 << "\n";
1640 }
1641 }
1642 });
1643 // log to redfish
1644 nmiDiagIntLog();
1645 std::cerr << "NMI out action completed\n";
1646 // reset Enable Property
1647 nmiSetEnablePorperty(false);
1648}
1649
1650static void nmiSourcePropertyMonitor(void)
1651{
1652 std::cerr << " NMI Source Property Monitor \n";
1653
1654 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1655 std::make_unique<sdbusplus::bus::match::match>(
1656 *conn,
1657 "type='signal',interface='org.freedesktop.DBus.Properties',"
1658 "member='PropertiesChanged',arg0namespace='com.intel.Control."
1659 "NMISource'",
1660 [](sdbusplus::message::message& msg) {
1661 std::string interfaceName;
1662 boost::container::flat_map<std::string,
1663 std::variant<bool, std::string>>
1664 propertiesChanged;
1665 std::string state;
1666 bool value = true;
1667 try
1668 {
1669 msg.read(interfaceName, propertiesChanged);
1670 if (propertiesChanged.begin()->first == "Enabled")
1671 {
1672 value =
1673 std::get<bool>(propertiesChanged.begin()->second);
1674 std::cerr
1675 << " NMI Enabled propertiesChanged value: " << value
1676 << "\n";
1677 nmiEnabled = value;
1678 if (nmiEnabled)
1679 {
1680 nmiReset();
1681 }
1682 }
1683 }
1684 catch (std::exception& e)
1685 {
1686 std::cerr << "Unable to read NMI source\n";
1687 return;
1688 }
1689 });
1690}
1691
1692static void setNmiSource()
1693{
1694 conn->async_method_call(
1695 [](boost::system::error_code ec) {
1696 if (ec)
1697 {
1698 std::cerr << "failed to set NMI source\n";
1699 }
1700 },
1701 "xyz.openbmc_project.Settings", "/com/intel/control/NMISource",
1702 "org.freedesktop.DBus.Properties", "Set", "com.intel.Control.NMISource",
1703 "BMCSource",
1704 std::variant<std::string>{
1705 "com.intel.Control.NMISource.BMCSourceSignal.FpBtn"});
1706 // set Enable Property
1707 nmiSetEnablePorperty(true);
1708}
1709
1710static void nmiButtonHandler()
1711{
1712 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1713
1714 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1715 {
1716 nmiButtonPressLog();
1717 nmiButtonIface->set_property("ButtonPressed", true);
1718 if (nmiButtonMasked)
1719 {
1720 std::cerr << "NMI button press masked\n";
1721 }
1722 else
1723 {
1724 setNmiSource();
1725 }
1726 }
1727 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1728 {
1729 nmiButtonIface->set_property("ButtonPressed", false);
1730 }
1731 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1732 [](const boost::system::error_code ec) {
1733 if (ec)
1734 {
1735 std::cerr << "NMI button handler error: "
1736 << ec.message() << "\n";
1737 return;
1738 }
1739 nmiButtonHandler();
1740 });
1741}
1742
1743static void idButtonHandler()
1744{
1745 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1746
1747 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1748 {
1749 idButtonIface->set_property("ButtonPressed", true);
1750 }
1751 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1752 {
1753 idButtonIface->set_property("ButtonPressed", false);
1754 }
1755 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1756 [](const boost::system::error_code& ec) {
1757 if (ec)
1758 {
1759 std::cerr << "ID button handler error: "
1760 << ec.message() << "\n";
1761 return;
1762 }
1763 idButtonHandler();
1764 });
1765}
1766
1767static void postCompleteHandler()
1768{
1769 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1770
1771 bool postComplete =
1772 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
1773 std::cerr << "POST complete value changed: " << postComplete << "\n";
1774 if (postComplete)
1775 {
1776 osIface->set_property("OperatingSystemState", std::string("Standby"));
1777 resetInProgress = false;
1778 }
1779 else
1780 {
1781 osIface->set_property("OperatingSystemState", std::string("Inactive"));
1782 // Set the restart cause if POST complete de-asserted by host software
1783 if (powerState == PowerState::on && !resetInProgress)
1784 {
1785 resetInProgress = true;
1786 setRestartCause(RestartCause::softReset);
1787 }
1788 }
1789 postCompleteEvent.async_wait(
1790 boost::asio::posix::stream_descriptor::wait_read,
1791 [](const boost::system::error_code ec) {
1792 if (ec)
1793 {
1794 std::cerr << "POST complete handler error: " << ec.message()
1795 << "\n";
1796 return;
1797 }
1798 postCompleteHandler();
1799 });
1800}
1801} // namespace power_control
1802
1803int main(int argc, char* argv[])
1804{
1805 std::cerr << "Start Chassis power control service...\n";
1806 power_control::conn =
1807 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1808
1809 // Request all the dbus names
1810 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1811 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
1812 power_control::conn->request_name(
1813 "xyz.openbmc_project.State.OperatingSystem");
1814 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
1815
1816 // Request PS_PWROK GPIO events
1817 if (!power_control::requestGPIOEvents(
1818 "PS_PWROK", power_control::psPowerOKHandler,
1819 power_control::psPowerOKLine, power_control::psPowerOKEvent))
1820 {
1821 return -1;
1822 }
1823
1824 // Request SIO_POWER_GOOD GPIO events
1825 if (!power_control::requestGPIOEvents(
1826 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
1827 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
1828 {
1829 return -1;
1830 }
1831
1832 // Request SIO_ONCONTROL GPIO events
1833 if (!power_control::requestGPIOEvents(
1834 "SIO_ONCONTROL", power_control::sioOnControlHandler,
1835 power_control::sioOnControlLine, power_control::sioOnControlEvent))
1836 {
1837 return -1;
1838 }
1839
1840 // Request SIO_S5 GPIO events
1841 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
1842 power_control::sioS5Line,
1843 power_control::sioS5Event))
1844 {
1845 return -1;
1846 }
1847
1848 // Request POWER_BUTTON GPIO events
1849 if (!power_control::requestGPIOEvents(
1850 "POWER_BUTTON", power_control::powerButtonHandler,
1851 power_control::powerButtonLine, power_control::powerButtonEvent))
1852 {
1853 return -1;
1854 }
1855
1856 // Request RESET_BUTTON GPIO events
1857 if (!power_control::requestGPIOEvents(
1858 "RESET_BUTTON", power_control::resetButtonHandler,
1859 power_control::resetButtonLine, power_control::resetButtonEvent))
1860 {
1861 return -1;
1862 }
1863
1864 // Request NMI_BUTTON GPIO events
1865 if (!power_control::requestGPIOEvents(
1866 "NMI_BUTTON", power_control::nmiButtonHandler,
1867 power_control::nmiButtonLine, power_control::nmiButtonEvent))
1868 {
1869 return -1;
1870 }
1871
1872 // Request ID_BUTTON GPIO events
1873 if (!power_control::requestGPIOEvents(
1874 "ID_BUTTON", power_control::idButtonHandler,
1875 power_control::idButtonLine, power_control::idButtonEvent))
1876 {
1877 return -1;
1878 }
1879
1880 // Request POST_COMPLETE GPIO events
1881 if (!power_control::requestGPIOEvents(
1882 "POST_COMPLETE", power_control::postCompleteHandler,
1883 power_control::postCompleteLine, power_control::postCompleteEvent))
1884 {
1885 return -1;
1886 }
1887
1888 // initialize NMI_OUT GPIO.
1889 if (!power_control::setGPIOOutput(power_control::nmiOutName, 0,
1890 power_control::nmiOutLine))
1891 {
1892 return -1;
1893 }
1894
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001895 // Initialize POWER_OUT and RESET_OUT GPIO.
1896 gpiod::line line;
1897 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
1898 {
1899 return -1;
1900 }
1901
1902 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
1903 {
1904 return -1;
1905 }
1906
1907 // Release line
1908 line.reset();
1909
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001910 // Initialize the power state
1911 power_control::powerState = power_control::PowerState::off;
1912 // Check power good
1913 if (power_control::psPowerOKLine.get_value() > 0)
1914 {
1915 power_control::powerState = power_control::PowerState::on;
1916 }
1917
1918 // Initialize the power state storage
1919 if (power_control::initializePowerStateStorage() < 0)
1920 {
1921 return -1;
1922 }
1923
1924 // Check if we need to start the Power Restore policy
1925 power_control::powerRestorePolicyCheck();
1926
1927 power_control::nmiSourcePropertyMonitor();
1928
1929 std::cerr << "Initializing power state. ";
1930 power_control::logStateTransition(power_control::powerState);
1931
1932 // Power Control Service
1933 sdbusplus::asio::object_server hostServer =
1934 sdbusplus::asio::object_server(power_control::conn);
1935
1936 // Power Control Interface
1937 power_control::hostIface = hostServer.add_interface(
1938 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
1939
1940 power_control::hostIface->register_property(
1941 "RequestedHostTransition",
1942 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
1943 [](const std::string& requested, std::string& resp) {
1944 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
1945 {
1946 sendPowerControlEvent(
1947 power_control::Event::gracefulPowerOffRequest);
1948 }
1949 else if (requested ==
1950 "xyz.openbmc_project.State.Host.Transition.On")
1951 {
1952 sendPowerControlEvent(power_control::Event::powerOnRequest);
1953 setRestartCause(power_control::RestartCause::command);
1954 }
1955 else if (requested ==
1956 "xyz.openbmc_project.State.Host.Transition.Reboot")
1957 {
1958 sendPowerControlEvent(
1959 power_control::Event::gracefulPowerCycleRequest);
1960 setRestartCause(power_control::RestartCause::command);
1961 }
1962 else
1963 {
1964 std::cerr << "Unrecognized host state transition request.\n";
1965 throw std::invalid_argument("Unrecognized Transition Request");
1966 return 0;
1967 }
1968 resp = requested;
1969 return 1;
1970 });
1971 power_control::hostIface->register_property(
1972 "CurrentHostState",
1973 std::string(power_control::getHostState(power_control::powerState)));
1974
1975 power_control::currentHostStateMonitor();
1976
1977 power_control::hostIface->initialize();
1978
1979 // Chassis Control Service
1980 sdbusplus::asio::object_server chassisServer =
1981 sdbusplus::asio::object_server(power_control::conn);
1982
1983 // Chassis Control Interface
1984 power_control::chassisIface =
1985 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
1986 "xyz.openbmc_project.State.Chassis");
1987
1988 power_control::chassisIface->register_property(
1989 "RequestedPowerTransition",
1990 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
1991 [](const std::string& requested, std::string& resp) {
1992 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
1993 {
1994 sendPowerControlEvent(power_control::Event::powerOffRequest);
1995 }
1996 else if (requested ==
1997 "xyz.openbmc_project.State.Chassis.Transition.On")
1998 {
1999 sendPowerControlEvent(power_control::Event::powerOnRequest);
2000 setRestartCause(power_control::RestartCause::command);
2001 }
2002 else if (requested ==
2003 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2004 {
2005 sendPowerControlEvent(power_control::Event::powerCycleRequest);
2006 setRestartCause(power_control::RestartCause::command);
2007 }
2008 else if (requested ==
2009 "xyz.openbmc_project.State.Chassis.Transition.Reset")
2010 {
2011 setRestartCause(power_control::RestartCause::command);
2012 sendPowerControlEvent(power_control::Event::resetRequest);
2013 }
2014 else
2015 {
2016 std::cerr << "Unrecognized chassis state transition request.\n";
2017 throw std::invalid_argument("Unrecognized Transition Request");
2018 return 0;
2019 }
2020 resp = requested;
2021 return 1;
2022 });
2023 power_control::chassisIface->register_property(
2024 "CurrentPowerState",
2025 std::string(power_control::getChassisState(power_control::powerState)));
2026 power_control::chassisIface->register_property(
2027 "LastStateChangeTime", power_control::getCurrentTimeMs());
2028
2029 power_control::chassisIface->initialize();
2030
2031 // Buttons Service
2032 sdbusplus::asio::object_server buttonsServer =
2033 sdbusplus::asio::object_server(power_control::conn);
2034
2035 // Power Button Interface
2036 power_control::powerButtonIface = buttonsServer.add_interface(
2037 "/xyz/openbmc_project/chassis/buttons/power",
2038 "xyz.openbmc_project.Chassis.Buttons");
2039
2040 power_control::powerButtonIface->register_property(
2041 "ButtonMasked", false, [](const bool requested, bool& current) {
2042 if (requested)
2043 {
2044 if (power_control::powerButtonMask)
2045 {
2046 return 1;
2047 }
2048 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002049 power_control::powerOutName, 1,
2050 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002051 {
2052 throw std::runtime_error("Failed to request GPIO");
2053 return 0;
2054 }
2055 std::cerr << "Power Button Masked.\n";
2056 }
2057 else
2058 {
2059 if (!power_control::powerButtonMask)
2060 {
2061 return 1;
2062 }
2063 std::cerr << "Power Button Un-masked\n";
2064 power_control::powerButtonMask.reset();
2065 }
2066 // Update the mask setting
2067 current = requested;
2068 return 1;
2069 });
2070
2071 // Check power button state
2072 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2073 power_control::powerButtonIface->register_property("ButtonPressed",
2074 powerButtonPressed);
2075
2076 power_control::powerButtonIface->initialize();
2077
2078 // Reset Button Interface
2079 power_control::resetButtonIface = buttonsServer.add_interface(
2080 "/xyz/openbmc_project/chassis/buttons/reset",
2081 "xyz.openbmc_project.Chassis.Buttons");
2082
2083 power_control::resetButtonIface->register_property(
2084 "ButtonMasked", false, [](const bool requested, bool& current) {
2085 if (requested)
2086 {
2087 if (power_control::resetButtonMask)
2088 {
2089 return 1;
2090 }
2091 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002092 power_control::resetOutName, 1,
2093 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002094 {
2095 throw std::runtime_error("Failed to request GPIO");
2096 return 0;
2097 }
2098 std::cerr << "Reset Button Masked.\n";
2099 }
2100 else
2101 {
2102 if (!power_control::resetButtonMask)
2103 {
2104 return 1;
2105 }
2106 std::cerr << "Reset Button Un-masked\n";
2107 power_control::resetButtonMask.reset();
2108 }
2109 // Update the mask setting
2110 current = requested;
2111 return 1;
2112 });
2113
2114 // Check reset button state
2115 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2116 power_control::resetButtonIface->register_property("ButtonPressed",
2117 resetButtonPressed);
2118
2119 power_control::resetButtonIface->initialize();
2120
2121 // NMI Button Interface
2122 power_control::nmiButtonIface =
2123 buttonsServer.add_interface("/xyz/openbmc_project/chassis/buttons/nmi",
2124 "xyz.openbmc_project.Chassis.Buttons");
2125
2126 power_control::nmiButtonIface->register_property(
2127 "ButtonMasked", false, [](const bool requested, bool& current) {
2128 if (power_control::nmiButtonMasked == requested)
2129 {
2130 // NMI button mask is already set as requested, so no change
2131 return 1;
2132 }
2133 if (requested)
2134 {
2135 std::cerr << "NMI Button Masked.\n";
2136 power_control::nmiButtonMasked = true;
2137 }
2138 else
2139 {
2140 std::cerr << "NMI Button Un-masked.\n";
2141 power_control::nmiButtonMasked = false;
2142 }
2143 // Update the mask setting
2144 current = power_control::nmiButtonMasked;
2145 return 1;
2146 });
2147
2148 // Check NMI button state
2149 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2150 power_control::nmiButtonIface->register_property("ButtonPressed",
2151 nmiButtonPressed);
2152
2153 power_control::nmiButtonIface->initialize();
2154
2155 // ID Button Interface
2156 power_control::idButtonIface =
2157 buttonsServer.add_interface("/xyz/openbmc_project/chassis/buttons/id",
2158 "xyz.openbmc_project.Chassis.Buttons");
2159
2160 // Check ID button state
2161 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2162 power_control::idButtonIface->register_property("ButtonPressed",
2163 idButtonPressed);
2164
2165 power_control::idButtonIface->initialize();
2166
2167 // OS State Service
2168 sdbusplus::asio::object_server osServer =
2169 sdbusplus::asio::object_server(power_control::conn);
2170
2171 // OS State Interface
2172 power_control::osIface = osServer.add_interface(
2173 "/xyz/openbmc_project/state/os",
2174 "xyz.openbmc_project.State.OperatingSystem.Status");
2175
2176 // Get the initial OS state based on POST complete
2177 // 0: Asserted, OS state is "Standby" (ready to boot)
2178 // 1: De-Asserted, OS state is "Inactive"
2179 std::string osState = power_control::postCompleteLine.get_value() > 0
2180 ? "Inactive"
2181 : "Standby";
2182
2183 power_control::osIface->register_property("OperatingSystemState",
2184 std::string(osState));
2185
2186 power_control::osIface->initialize();
2187
2188 power_control::io.run();
2189
2190 return 0;
2191}