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