blob: b8497e0048a655459d3f1faf788272672d9a78f4 [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();
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301308 // POST_COMPLETE GPIO event is not working in some platforms
1309 // when power state is changed to OFF. This resulted in
1310 // 'OperatingSystemState' to stay at 'Standby', even though
1311 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1312 // if HostState is trurned to OFF.
1313 osIface->set_property("OperatingSystemState",
1314 std::string("Inactive"));
1315
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001316 // Set the restart cause set for this restart
1317 setRestartCause();
Yong Li8d660212019-12-27 10:18:10 +08001318 sd_journal_send("MESSAGE=Host system DC power is off",
1319 "PRIORITY=%i", LOG_INFO,
1320 "REDFISH_MESSAGE_ID=%s",
1321 "OpenBMC.0.1.DCPowerOff", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001322 }
1323 });
1324}
1325
1326static void sioPowerGoodWatchdogTimerStart()
1327{
1328 std::cerr << "SIO power good watchdog timer started\n";
1329 sioPowerGoodWatchdogTimer.expires_after(
1330 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1331 sioPowerGoodWatchdogTimer.async_wait(
1332 [](const boost::system::error_code ec) {
1333 if (ec)
1334 {
1335 // operation_aborted is expected if timer is canceled before
1336 // completion.
1337 if (ec != boost::asio::error::operation_aborted)
1338 {
1339 std::cerr << "SIO power good watchdog async_wait failed: "
1340 << ec.message() << "\n";
1341 }
1342 std::cerr << "SIO power good watchdog timer canceled\n";
1343 return;
1344 }
1345 std::cerr << "SIO power good watchdog timer completed\n";
1346 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1347 });
1348}
1349
1350static void powerStateOn(const Event event)
1351{
1352 logEvent(__FUNCTION__, event);
1353 switch (event)
1354 {
1355 case Event::psPowerOKDeAssert:
1356 setPowerState(PowerState::off);
1357 // DC power is unexpectedly lost, beep
1358 beep(beepPowerFail);
1359 break;
1360 case Event::sioS5Assert:
1361 setPowerState(PowerState::transitionToOff);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001362 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001363 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001364 case Event::postCompleteDeAssert:
1365 setPowerState(PowerState::checkForWarmReset);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001366 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001367 warmResetCheckTimerStart();
1368 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001369 case Event::powerButtonPressed:
1370 setPowerState(PowerState::gracefulTransitionToOff);
1371 gracefulPowerOffTimerStart();
1372 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001373 case Event::resetButtonPressed:
1374 setPowerState(PowerState::checkForWarmReset);
1375 warmResetCheckTimerStart();
1376 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001377 case Event::powerOffRequest:
1378 setPowerState(PowerState::transitionToOff);
1379 forcePowerOff();
1380 break;
1381 case Event::gracefulPowerOffRequest:
1382 setPowerState(PowerState::gracefulTransitionToOff);
1383 gracefulPowerOffTimerStart();
1384 gracefulPowerOff();
1385 break;
1386 case Event::powerCycleRequest:
1387 setPowerState(PowerState::transitionToCycleOff);
1388 forcePowerOff();
1389 break;
1390 case Event::gracefulPowerCycleRequest:
1391 setPowerState(PowerState::gracefulTransitionToCycleOff);
1392 gracefulPowerOffTimerStart();
1393 gracefulPowerOff();
1394 break;
1395 case Event::resetRequest:
1396 reset();
1397 break;
1398 default:
1399 std::cerr << "No action taken.\n";
1400 break;
1401 }
1402}
1403
1404static void powerStateWaitForPSPowerOK(const Event event)
1405{
1406 logEvent(__FUNCTION__, event);
1407 switch (event)
1408 {
1409 case Event::psPowerOKAssert:
1410 // Cancel any GPIO assertions held during the transition
1411 gpioAssertTimer.cancel();
1412 psPowerOKWatchdogTimer.cancel();
1413 sioPowerGoodWatchdogTimerStart();
1414 setPowerState(PowerState::waitForSIOPowerGood);
1415 break;
1416 case Event::psPowerOKWatchdogTimerExpired:
1417 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001418 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001419 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001420 case Event::sioPowerGoodAssert:
1421 psPowerOKWatchdogTimer.cancel();
1422 setPowerState(PowerState::on);
1423 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001424 default:
1425 std::cerr << "No action taken.\n";
1426 break;
1427 }
1428}
1429
1430static void powerStateWaitForSIOPowerGood(const Event event)
1431{
1432 logEvent(__FUNCTION__, event);
1433 switch (event)
1434 {
1435 case Event::sioPowerGoodAssert:
1436 sioPowerGoodWatchdogTimer.cancel();
1437 setPowerState(PowerState::on);
1438 break;
1439 case Event::sioPowerGoodWatchdogTimerExpired:
1440 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001441 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001442 forcePowerOff();
1443 break;
1444 default:
1445 std::cerr << "No action taken.\n";
1446 break;
1447 }
1448}
1449
1450static void powerStateFailedTransitionToOn(const Event event)
1451{
1452 logEvent(__FUNCTION__, event);
1453 switch (event)
1454 {
1455 case Event::psPowerOKAssert:
1456 // We're in a failure state, so don't allow the system to turn on
1457 // without a user request
1458 forcePowerOff();
1459 break;
1460 case Event::psPowerOKDeAssert:
1461 // Cancel any GPIO assertions held during the transition
1462 gpioAssertTimer.cancel();
1463 break;
1464 case Event::powerButtonPressed:
1465 psPowerOKWatchdogTimerStart();
1466 setPowerState(PowerState::waitForPSPowerOK);
1467 break;
1468 case Event::powerOnRequest:
1469 psPowerOKWatchdogTimerStart();
1470 setPowerState(PowerState::waitForPSPowerOK);
1471 powerOn();
1472 break;
1473 default:
1474 std::cerr << "No action taken.\n";
1475 break;
1476 }
1477}
1478
1479static void powerStateOff(const Event event)
1480{
1481 logEvent(__FUNCTION__, event);
1482 switch (event)
1483 {
1484 case Event::psPowerOKAssert:
1485 setPowerState(PowerState::waitForSIOPowerGood);
1486 break;
1487 case Event::sioS5DeAssert:
1488 setPowerState(PowerState::waitForPSPowerOK);
1489 break;
1490 case Event::powerButtonPressed:
1491 psPowerOKWatchdogTimerStart();
1492 setPowerState(PowerState::waitForPSPowerOK);
1493 break;
1494 case Event::powerOnRequest:
1495 psPowerOKWatchdogTimerStart();
1496 setPowerState(PowerState::waitForPSPowerOK);
1497 powerOn();
1498 break;
1499 default:
1500 std::cerr << "No action taken.\n";
1501 break;
1502 }
1503}
1504
1505static void powerStateTransitionToOff(const Event event)
1506{
1507 logEvent(__FUNCTION__, event);
1508 switch (event)
1509 {
1510 case Event::psPowerOKDeAssert:
1511 // Cancel any GPIO assertions held during the transition
1512 gpioAssertTimer.cancel();
1513 setPowerState(PowerState::off);
1514 break;
1515 default:
1516 std::cerr << "No action taken.\n";
1517 break;
1518 }
1519}
1520
1521static void powerStateGracefulTransitionToOff(const Event event)
1522{
1523 logEvent(__FUNCTION__, event);
1524 switch (event)
1525 {
1526 case Event::psPowerOKDeAssert:
1527 gracefulPowerOffTimer.cancel();
1528 setPowerState(PowerState::off);
1529 break;
1530 case Event::gracefulPowerOffTimerExpired:
1531 setPowerState(PowerState::on);
1532 break;
1533 default:
1534 std::cerr << "No action taken.\n";
1535 break;
1536 }
1537}
1538
1539static void powerStateCycleOff(const Event event)
1540{
1541 logEvent(__FUNCTION__, event);
1542 switch (event)
1543 {
1544 case Event::powerCycleTimerExpired:
1545 psPowerOKWatchdogTimerStart();
1546 setPowerState(PowerState::waitForPSPowerOK);
1547 powerOn();
1548 break;
1549 default:
1550 std::cerr << "No action taken.\n";
1551 break;
1552 }
1553}
1554
1555static void powerStateTransitionToCycleOff(const Event event)
1556{
1557 logEvent(__FUNCTION__, event);
1558 switch (event)
1559 {
1560 case Event::psPowerOKDeAssert:
1561 // Cancel any GPIO assertions held during the transition
1562 gpioAssertTimer.cancel();
1563 setPowerState(PowerState::cycleOff);
1564 powerCycleTimerStart();
1565 break;
1566 default:
1567 std::cerr << "No action taken.\n";
1568 break;
1569 }
1570}
1571
1572static void powerStateGracefulTransitionToCycleOff(const Event event)
1573{
1574 logEvent(__FUNCTION__, event);
1575 switch (event)
1576 {
1577 case Event::psPowerOKDeAssert:
1578 gracefulPowerOffTimer.cancel();
1579 setPowerState(PowerState::cycleOff);
1580 powerCycleTimerStart();
1581 break;
1582 case Event::gracefulPowerOffTimerExpired:
1583 setPowerState(PowerState::on);
1584 break;
1585 default:
1586 std::cerr << "No action taken.\n";
1587 break;
1588 }
1589}
1590
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001591static void powerStateCheckForWarmReset(const Event event)
1592{
1593 logEvent(__FUNCTION__, event);
1594 switch (event)
1595 {
1596 case Event::sioS5Assert:
1597 warmResetCheckTimer.cancel();
1598 setPowerState(PowerState::transitionToOff);
1599 break;
1600 case Event::warmResetDetected:
1601 setPowerState(PowerState::on);
1602 break;
P.K. Lee344dae82019-11-27 16:35:05 +08001603 case Event::psPowerOKDeAssert:
1604 warmResetCheckTimer.cancel();
1605 setPowerState(PowerState::off);
1606 // DC power is unexpectedly lost, beep
1607 beep(beepPowerFail);
1608 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001609 default:
1610 std::cerr << "No action taken.\n";
1611 break;
1612 }
1613}
1614
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001615static void psPowerOKHandler()
1616{
1617 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1618
1619 Event powerControlEvent =
1620 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1621 ? Event::psPowerOKAssert
1622 : Event::psPowerOKDeAssert;
1623
1624 sendPowerControlEvent(powerControlEvent);
1625 psPowerOKEvent.async_wait(
1626 boost::asio::posix::stream_descriptor::wait_read,
1627 [](const boost::system::error_code ec) {
1628 if (ec)
1629 {
1630 std::cerr << "power supply power OK handler error: "
1631 << ec.message() << "\n";
1632 return;
1633 }
1634 psPowerOKHandler();
1635 });
1636}
1637
1638static void sioPowerGoodHandler()
1639{
1640 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1641
1642 Event powerControlEvent =
1643 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1644 ? Event::sioPowerGoodAssert
1645 : Event::sioPowerGoodDeAssert;
1646
1647 sendPowerControlEvent(powerControlEvent);
1648 sioPowerGoodEvent.async_wait(
1649 boost::asio::posix::stream_descriptor::wait_read,
1650 [](const boost::system::error_code ec) {
1651 if (ec)
1652 {
1653 std::cerr << "SIO power good handler error: " << ec.message()
1654 << "\n";
1655 return;
1656 }
1657 sioPowerGoodHandler();
1658 });
1659}
1660
1661static void sioOnControlHandler()
1662{
1663 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1664
1665 bool sioOnControl =
1666 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1667 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1668 sioOnControlEvent.async_wait(
1669 boost::asio::posix::stream_descriptor::wait_read,
1670 [](const boost::system::error_code ec) {
1671 if (ec)
1672 {
1673 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1674 << "\n";
1675 return;
1676 }
1677 sioOnControlHandler();
1678 });
1679}
1680
1681static void sioS5Handler()
1682{
1683 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1684
1685 Event powerControlEvent =
1686 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1687 ? Event::sioS5Assert
1688 : Event::sioS5DeAssert;
1689
1690 sendPowerControlEvent(powerControlEvent);
1691 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1692 [](const boost::system::error_code ec) {
1693 if (ec)
1694 {
1695 std::cerr << "SIO S5 handler error: "
1696 << ec.message() << "\n";
1697 return;
1698 }
1699 sioS5Handler();
1700 });
1701}
1702
1703static void powerButtonHandler()
1704{
1705 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1706
1707 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1708 {
1709 powerButtonPressLog();
1710 powerButtonIface->set_property("ButtonPressed", true);
1711 if (!powerButtonMask)
1712 {
1713 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001714 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001715 }
1716 else
1717 {
1718 std::cerr << "power button press masked\n";
1719 }
1720 }
1721 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1722 {
1723 powerButtonIface->set_property("ButtonPressed", false);
1724 }
1725 powerButtonEvent.async_wait(
1726 boost::asio::posix::stream_descriptor::wait_read,
1727 [](const boost::system::error_code ec) {
1728 if (ec)
1729 {
1730 std::cerr << "power button handler error: " << ec.message()
1731 << "\n";
1732 return;
1733 }
1734 powerButtonHandler();
1735 });
1736}
1737
1738static void resetButtonHandler()
1739{
1740 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1741
1742 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1743 {
1744 resetButtonPressLog();
1745 resetButtonIface->set_property("ButtonPressed", true);
1746 if (!resetButtonMask)
1747 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001748 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001749 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001750 }
1751 else
1752 {
1753 std::cerr << "reset button press masked\n";
1754 }
1755 }
1756 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1757 {
1758 resetButtonIface->set_property("ButtonPressed", false);
1759 }
1760 resetButtonEvent.async_wait(
1761 boost::asio::posix::stream_descriptor::wait_read,
1762 [](const boost::system::error_code ec) {
1763 if (ec)
1764 {
1765 std::cerr << "reset button handler error: " << ec.message()
1766 << "\n";
1767 return;
1768 }
1769 resetButtonHandler();
1770 });
1771}
1772
1773static void nmiSetEnablePorperty(bool value)
1774{
1775 conn->async_method_call(
1776 [](boost::system::error_code ec) {
1777 if (ec)
1778 {
1779 std::cerr << "failed to set NMI source\n";
1780 }
1781 },
Chen Yugang303bd582019-11-01 08:45:06 +08001782 "xyz.openbmc_project.Settings",
1783 "/xyz/openbmc_project/Chassis/Control/NMISource",
1784 "org.freedesktop.DBus.Properties", "Set",
1785 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
1786 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001787}
1788
1789static void nmiReset(void)
1790{
1791 static constexpr const uint8_t value = 1;
1792 const static constexpr int nmiOutPulseTimeMs = 200;
1793
1794 std::cerr << "NMI out action \n";
1795 nmiOutLine.set_value(value);
1796 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1797 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1798 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1799 // restore the NMI_OUT GPIO line back to the opposite value
1800 nmiOutLine.set_value(!value);
1801 std::cerr << nmiOutName << " released\n";
1802 if (ec)
1803 {
1804 // operation_aborted is expected if timer is canceled before
1805 // completion.
1806 if (ec != boost::asio::error::operation_aborted)
1807 {
1808 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1809 << "\n";
1810 }
1811 }
1812 });
1813 // log to redfish
1814 nmiDiagIntLog();
1815 std::cerr << "NMI out action completed\n";
1816 // reset Enable Property
1817 nmiSetEnablePorperty(false);
1818}
1819
1820static void nmiSourcePropertyMonitor(void)
1821{
1822 std::cerr << " NMI Source Property Monitor \n";
1823
1824 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1825 std::make_unique<sdbusplus::bus::match::match>(
1826 *conn,
1827 "type='signal',interface='org.freedesktop.DBus.Properties',"
Chen Yugang303bd582019-11-01 08:45:06 +08001828 "member='PropertiesChanged',arg0namespace='xyz.openbmc_project."
1829 "Chassis.Control."
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001830 "NMISource'",
1831 [](sdbusplus::message::message& msg) {
1832 std::string interfaceName;
1833 boost::container::flat_map<std::string,
1834 std::variant<bool, std::string>>
1835 propertiesChanged;
1836 std::string state;
1837 bool value = true;
1838 try
1839 {
1840 msg.read(interfaceName, propertiesChanged);
1841 if (propertiesChanged.begin()->first == "Enabled")
1842 {
1843 value =
1844 std::get<bool>(propertiesChanged.begin()->second);
1845 std::cerr
1846 << " NMI Enabled propertiesChanged value: " << value
1847 << "\n";
1848 nmiEnabled = value;
1849 if (nmiEnabled)
1850 {
1851 nmiReset();
1852 }
1853 }
1854 }
1855 catch (std::exception& e)
1856 {
1857 std::cerr << "Unable to read NMI source\n";
1858 return;
1859 }
1860 });
1861}
1862
1863static void setNmiSource()
1864{
1865 conn->async_method_call(
1866 [](boost::system::error_code ec) {
1867 if (ec)
1868 {
1869 std::cerr << "failed to set NMI source\n";
1870 }
1871 },
Chen Yugang303bd582019-11-01 08:45:06 +08001872 "xyz.openbmc_project.Settings",
1873 "/xyz/openbmc_project/Chassis/Control/NMISource",
1874 "org.freedesktop.DBus.Properties", "Set",
1875 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
1876 std::variant<std::string>{"xyz.openbmc_project.Chassis.Control."
1877 "NMISource.BMCSourceSignal.FpBtn"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001878 // set Enable Property
1879 nmiSetEnablePorperty(true);
1880}
1881
1882static void nmiButtonHandler()
1883{
1884 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1885
1886 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1887 {
1888 nmiButtonPressLog();
1889 nmiButtonIface->set_property("ButtonPressed", true);
1890 if (nmiButtonMasked)
1891 {
1892 std::cerr << "NMI button press masked\n";
1893 }
1894 else
1895 {
1896 setNmiSource();
1897 }
1898 }
1899 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1900 {
1901 nmiButtonIface->set_property("ButtonPressed", false);
1902 }
1903 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1904 [](const boost::system::error_code ec) {
1905 if (ec)
1906 {
1907 std::cerr << "NMI button handler error: "
1908 << ec.message() << "\n";
1909 return;
1910 }
1911 nmiButtonHandler();
1912 });
1913}
1914
1915static void idButtonHandler()
1916{
1917 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1918
1919 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1920 {
1921 idButtonIface->set_property("ButtonPressed", true);
1922 }
1923 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1924 {
1925 idButtonIface->set_property("ButtonPressed", false);
1926 }
1927 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1928 [](const boost::system::error_code& ec) {
1929 if (ec)
1930 {
1931 std::cerr << "ID button handler error: "
1932 << ec.message() << "\n";
1933 return;
1934 }
1935 idButtonHandler();
1936 });
1937}
1938
1939static void postCompleteHandler()
1940{
1941 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1942
1943 bool postComplete =
1944 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001945 if (postComplete)
1946 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001947 sendPowerControlEvent(Event::postCompleteAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001948 osIface->set_property("OperatingSystemState", std::string("Standby"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001949 }
1950 else
1951 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001952 sendPowerControlEvent(Event::postCompleteDeAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001953 osIface->set_property("OperatingSystemState", std::string("Inactive"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001954 }
1955 postCompleteEvent.async_wait(
1956 boost::asio::posix::stream_descriptor::wait_read,
1957 [](const boost::system::error_code ec) {
1958 if (ec)
1959 {
1960 std::cerr << "POST complete handler error: " << ec.message()
1961 << "\n";
1962 return;
1963 }
1964 postCompleteHandler();
1965 });
1966}
1967} // namespace power_control
1968
1969int main(int argc, char* argv[])
1970{
1971 std::cerr << "Start Chassis power control service...\n";
1972 power_control::conn =
1973 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1974
1975 // Request all the dbus names
1976 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1977 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
1978 power_control::conn->request_name(
1979 "xyz.openbmc_project.State.OperatingSystem");
1980 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
Chen Yugang174ec662019-08-19 19:58:49 +08001981 power_control::conn->request_name("xyz.openbmc_project.Control.Host.NMI");
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001982 power_control::conn->request_name(
1983 "xyz.openbmc_project.Control.Host.RestartCause");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001984
1985 // Request PS_PWROK GPIO events
1986 if (!power_control::requestGPIOEvents(
1987 "PS_PWROK", power_control::psPowerOKHandler,
1988 power_control::psPowerOKLine, power_control::psPowerOKEvent))
1989 {
1990 return -1;
1991 }
1992
1993 // Request SIO_POWER_GOOD GPIO events
1994 if (!power_control::requestGPIOEvents(
1995 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
1996 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
1997 {
1998 return -1;
1999 }
2000
2001 // Request SIO_ONCONTROL GPIO events
2002 if (!power_control::requestGPIOEvents(
2003 "SIO_ONCONTROL", power_control::sioOnControlHandler,
2004 power_control::sioOnControlLine, power_control::sioOnControlEvent))
2005 {
2006 return -1;
2007 }
2008
2009 // Request SIO_S5 GPIO events
2010 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
2011 power_control::sioS5Line,
2012 power_control::sioS5Event))
2013 {
2014 return -1;
2015 }
2016
2017 // Request POWER_BUTTON GPIO events
2018 if (!power_control::requestGPIOEvents(
2019 "POWER_BUTTON", power_control::powerButtonHandler,
2020 power_control::powerButtonLine, power_control::powerButtonEvent))
2021 {
2022 return -1;
2023 }
2024
2025 // Request RESET_BUTTON GPIO events
2026 if (!power_control::requestGPIOEvents(
2027 "RESET_BUTTON", power_control::resetButtonHandler,
2028 power_control::resetButtonLine, power_control::resetButtonEvent))
2029 {
2030 return -1;
2031 }
2032
2033 // Request NMI_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08002034 power_control::requestGPIOEvents(
2035 "NMI_BUTTON", power_control::nmiButtonHandler,
2036 power_control::nmiButtonLine, power_control::nmiButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002037
2038 // Request ID_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08002039 power_control::requestGPIOEvents(
2040 "ID_BUTTON", power_control::idButtonHandler,
2041 power_control::idButtonLine, power_control::idButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002042
2043 // Request POST_COMPLETE GPIO events
2044 if (!power_control::requestGPIOEvents(
2045 "POST_COMPLETE", power_control::postCompleteHandler,
2046 power_control::postCompleteLine, power_control::postCompleteEvent))
2047 {
2048 return -1;
2049 }
2050
2051 // initialize NMI_OUT GPIO.
Vijay Khemka33a532d2019-11-14 16:50:35 -08002052 power_control::setGPIOOutput(power_control::nmiOutName, 0,
2053 power_control::nmiOutLine);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002054
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002055 // Initialize POWER_OUT and RESET_OUT GPIO.
2056 gpiod::line line;
2057 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
2058 {
2059 return -1;
2060 }
2061
2062 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
2063 {
2064 return -1;
2065 }
2066
2067 // Release line
2068 line.reset();
2069
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002070 // Initialize the power state
2071 power_control::powerState = power_control::PowerState::off;
2072 // Check power good
2073 if (power_control::psPowerOKLine.get_value() > 0)
2074 {
2075 power_control::powerState = power_control::PowerState::on;
2076 }
2077
2078 // Initialize the power state storage
2079 if (power_control::initializePowerStateStorage() < 0)
2080 {
2081 return -1;
2082 }
2083
2084 // Check if we need to start the Power Restore policy
2085 power_control::powerRestorePolicyCheck();
2086
Vijay Khemka33a532d2019-11-14 16:50:35 -08002087 if (power_control::nmiOutLine)
2088 power_control::nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002089
2090 std::cerr << "Initializing power state. ";
2091 power_control::logStateTransition(power_control::powerState);
2092
2093 // Power Control Service
2094 sdbusplus::asio::object_server hostServer =
2095 sdbusplus::asio::object_server(power_control::conn);
2096
2097 // Power Control Interface
2098 power_control::hostIface = hostServer.add_interface(
2099 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
2100
2101 power_control::hostIface->register_property(
2102 "RequestedHostTransition",
2103 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2104 [](const std::string& requested, std::string& resp) {
2105 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2106 {
2107 sendPowerControlEvent(
2108 power_control::Event::gracefulPowerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002109 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002110 }
2111 else if (requested ==
2112 "xyz.openbmc_project.State.Host.Transition.On")
2113 {
2114 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002115 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002116 }
2117 else if (requested ==
2118 "xyz.openbmc_project.State.Host.Transition.Reboot")
2119 {
2120 sendPowerControlEvent(
2121 power_control::Event::gracefulPowerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002122 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002123 }
2124 else
2125 {
2126 std::cerr << "Unrecognized host state transition request.\n";
2127 throw std::invalid_argument("Unrecognized Transition Request");
2128 return 0;
2129 }
2130 resp = requested;
2131 return 1;
2132 });
2133 power_control::hostIface->register_property(
2134 "CurrentHostState",
2135 std::string(power_control::getHostState(power_control::powerState)));
2136
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002137 power_control::hostIface->initialize();
2138
2139 // Chassis Control Service
2140 sdbusplus::asio::object_server chassisServer =
2141 sdbusplus::asio::object_server(power_control::conn);
2142
2143 // Chassis Control Interface
2144 power_control::chassisIface =
2145 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
2146 "xyz.openbmc_project.State.Chassis");
2147
2148 power_control::chassisIface->register_property(
2149 "RequestedPowerTransition",
2150 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2151 [](const std::string& requested, std::string& resp) {
2152 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2153 {
2154 sendPowerControlEvent(power_control::Event::powerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002155 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002156 }
2157 else if (requested ==
2158 "xyz.openbmc_project.State.Chassis.Transition.On")
2159 {
2160 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002161 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002162 }
2163 else if (requested ==
2164 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2165 {
2166 sendPowerControlEvent(power_control::Event::powerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002167 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002168 }
2169 else if (requested ==
2170 "xyz.openbmc_project.State.Chassis.Transition.Reset")
2171 {
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002172 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002173 sendPowerControlEvent(power_control::Event::resetRequest);
2174 }
2175 else
2176 {
2177 std::cerr << "Unrecognized chassis state transition request.\n";
2178 throw std::invalid_argument("Unrecognized Transition Request");
2179 return 0;
2180 }
2181 resp = requested;
2182 return 1;
2183 });
2184 power_control::chassisIface->register_property(
2185 "CurrentPowerState",
2186 std::string(power_control::getChassisState(power_control::powerState)));
2187 power_control::chassisIface->register_property(
2188 "LastStateChangeTime", power_control::getCurrentTimeMs());
2189
2190 power_control::chassisIface->initialize();
2191
2192 // Buttons Service
2193 sdbusplus::asio::object_server buttonsServer =
2194 sdbusplus::asio::object_server(power_control::conn);
2195
2196 // Power Button Interface
2197 power_control::powerButtonIface = buttonsServer.add_interface(
2198 "/xyz/openbmc_project/chassis/buttons/power",
2199 "xyz.openbmc_project.Chassis.Buttons");
2200
2201 power_control::powerButtonIface->register_property(
2202 "ButtonMasked", false, [](const bool requested, bool& current) {
2203 if (requested)
2204 {
2205 if (power_control::powerButtonMask)
2206 {
2207 return 1;
2208 }
2209 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002210 power_control::powerOutName, 1,
2211 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002212 {
2213 throw std::runtime_error("Failed to request GPIO");
2214 return 0;
2215 }
2216 std::cerr << "Power Button Masked.\n";
2217 }
2218 else
2219 {
2220 if (!power_control::powerButtonMask)
2221 {
2222 return 1;
2223 }
2224 std::cerr << "Power Button Un-masked\n";
2225 power_control::powerButtonMask.reset();
2226 }
2227 // Update the mask setting
2228 current = requested;
2229 return 1;
2230 });
2231
2232 // Check power button state
2233 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2234 power_control::powerButtonIface->register_property("ButtonPressed",
2235 powerButtonPressed);
2236
2237 power_control::powerButtonIface->initialize();
2238
2239 // Reset Button Interface
2240 power_control::resetButtonIface = buttonsServer.add_interface(
2241 "/xyz/openbmc_project/chassis/buttons/reset",
2242 "xyz.openbmc_project.Chassis.Buttons");
2243
2244 power_control::resetButtonIface->register_property(
2245 "ButtonMasked", false, [](const bool requested, bool& current) {
2246 if (requested)
2247 {
2248 if (power_control::resetButtonMask)
2249 {
2250 return 1;
2251 }
2252 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002253 power_control::resetOutName, 1,
2254 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002255 {
2256 throw std::runtime_error("Failed to request GPIO");
2257 return 0;
2258 }
2259 std::cerr << "Reset Button Masked.\n";
2260 }
2261 else
2262 {
2263 if (!power_control::resetButtonMask)
2264 {
2265 return 1;
2266 }
2267 std::cerr << "Reset Button Un-masked\n";
2268 power_control::resetButtonMask.reset();
2269 }
2270 // Update the mask setting
2271 current = requested;
2272 return 1;
2273 });
2274
2275 // Check reset button state
2276 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2277 power_control::resetButtonIface->register_property("ButtonPressed",
2278 resetButtonPressed);
2279
2280 power_control::resetButtonIface->initialize();
2281
Vijay Khemka33a532d2019-11-14 16:50:35 -08002282 if (power_control::nmiButtonLine)
2283 {
2284 // NMI Button Interface
2285 power_control::nmiButtonIface = buttonsServer.add_interface(
2286 "/xyz/openbmc_project/chassis/buttons/nmi",
2287 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002288
Vijay Khemka33a532d2019-11-14 16:50:35 -08002289 power_control::nmiButtonIface->register_property(
2290 "ButtonMasked", false, [](const bool requested, bool& current) {
2291 if (power_control::nmiButtonMasked == requested)
2292 {
2293 // NMI button mask is already set as requested, so no change
2294 return 1;
2295 }
2296 if (requested)
2297 {
2298 std::cerr << "NMI Button Masked.\n";
2299 power_control::nmiButtonMasked = true;
2300 }
2301 else
2302 {
2303 std::cerr << "NMI Button Un-masked.\n";
2304 power_control::nmiButtonMasked = false;
2305 }
2306 // Update the mask setting
2307 current = power_control::nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002308 return 1;
Vijay Khemka33a532d2019-11-14 16:50:35 -08002309 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002310
Vijay Khemka33a532d2019-11-14 16:50:35 -08002311 // Check NMI button state
2312 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2313 power_control::nmiButtonIface->register_property("ButtonPressed",
2314 nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002315
Vijay Khemka33a532d2019-11-14 16:50:35 -08002316 power_control::nmiButtonIface->initialize();
2317 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002318
Vijay Khemka33a532d2019-11-14 16:50:35 -08002319 if (power_control::nmiOutLine)
2320 {
2321 // NMI out Service
2322 sdbusplus::asio::object_server nmiOutServer =
2323 sdbusplus::asio::object_server(power_control::conn);
Chen Yugang174ec662019-08-19 19:58:49 +08002324
Vijay Khemka33a532d2019-11-14 16:50:35 -08002325 // NMI out Interface
2326 power_control::nmiOutIface =
2327 nmiOutServer.add_interface("/xyz/openbmc_project/control/host0/nmi",
2328 "xyz.openbmc_project.Control.Host.NMI");
2329 power_control::nmiOutIface->register_method("NMI",
2330 power_control::nmiReset);
2331 power_control::nmiOutIface->initialize();
2332 }
Chen Yugang174ec662019-08-19 19:58:49 +08002333
Vijay Khemka33a532d2019-11-14 16:50:35 -08002334 if (power_control::idButtonLine)
2335 {
2336 // ID Button Interface
2337 power_control::idButtonIface = buttonsServer.add_interface(
2338 "/xyz/openbmc_project/chassis/buttons/id",
2339 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002340
Vijay Khemka33a532d2019-11-14 16:50:35 -08002341 // Check ID button state
2342 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2343 power_control::idButtonIface->register_property("ButtonPressed",
2344 idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002345
Vijay Khemka33a532d2019-11-14 16:50:35 -08002346 power_control::idButtonIface->initialize();
2347 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002348
2349 // OS State Service
2350 sdbusplus::asio::object_server osServer =
2351 sdbusplus::asio::object_server(power_control::conn);
2352
2353 // OS State Interface
2354 power_control::osIface = osServer.add_interface(
2355 "/xyz/openbmc_project/state/os",
2356 "xyz.openbmc_project.State.OperatingSystem.Status");
2357
2358 // Get the initial OS state based on POST complete
2359 // 0: Asserted, OS state is "Standby" (ready to boot)
2360 // 1: De-Asserted, OS state is "Inactive"
2361 std::string osState = power_control::postCompleteLine.get_value() > 0
2362 ? "Inactive"
2363 : "Standby";
2364
2365 power_control::osIface->register_property("OperatingSystemState",
2366 std::string(osState));
2367
2368 power_control::osIface->initialize();
2369
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002370 // Restart Cause Service
2371 sdbusplus::asio::object_server restartCauseServer =
2372 sdbusplus::asio::object_server(power_control::conn);
2373
2374 // Restart Cause Interface
2375 power_control::restartCauseIface = restartCauseServer.add_interface(
2376 "/xyz/openbmc_project/control/host0/restart_cause",
2377 "xyz.openbmc_project.Control.Host.RestartCause");
2378
2379 power_control::restartCauseIface->register_property(
2380 "RestartCause",
2381 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
2382
2383 power_control::restartCauseIface->register_property(
2384 "RequestedRestartCause",
2385 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
2386 [](const std::string& requested, std::string& resp) {
2387 if (requested ==
2388 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
2389 {
2390 power_control::addRestartCause(
2391 power_control::RestartCause::watchdog);
2392 }
2393 else
2394 {
2395 throw std::invalid_argument(
2396 "Unrecognized RestartCause Request");
2397 return 0;
2398 }
2399
2400 std::cerr << "RestartCause requested: " << requested << "\n";
2401 resp = requested;
2402 return 1;
2403 });
2404
2405 power_control::restartCauseIface->initialize();
2406
Yong Li8d660212019-12-27 10:18:10 +08002407 power_control::currentHostStateMonitor();
2408
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002409 power_control::io.run();
2410
2411 return 0;
2412}