blob: 20ea9cdd895373117167526a9a93cb141c75a4fb [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;
Jason M. Billsfc9408a2020-01-31 14:54:17 -080052const static constexpr int powerCycleTimeMs = 5000;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070053const 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}
Rashmi RV89f61312020-01-22 15:41:50 +0530517
518static void resetACBootProperty()
519{
520 if ((causeSet.contains(RestartCause::command)) ||
521 (causeSet.contains(RestartCause::softReset)))
522 {
523 conn->async_method_call(
524 [](boost::system::error_code ec) {
525 if (ec)
526 {
527 std::cerr << "failed to reset ACBoot property\n";
528 }
529 },
530 "xyz.openbmc_project.Settings",
531 "/xyz/openbmc_project/control/host0/ac_boot",
532 "org.freedesktop.DBus.Properties", "Set",
533 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
534 std::variant<std::string>{"False"});
535 }
536}
537
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700538static void setRestartCause()
539{
540 // Determine the actual restart cause based on the set of causes
541 std::string restartCause =
542 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
543 if (causeSet.contains(RestartCause::watchdog))
544 {
545 restartCause = getRestartCause(RestartCause::watchdog);
546 }
547 else if (causeSet.contains(RestartCause::command))
548 {
549 restartCause = getRestartCause(RestartCause::command);
550 }
551 else if (causeSet.contains(RestartCause::resetButton))
552 {
553 restartCause = getRestartCause(RestartCause::resetButton);
554 }
555 else if (causeSet.contains(RestartCause::powerButton))
556 {
557 restartCause = getRestartCause(RestartCause::powerButton);
558 }
559 else if (causeSet.contains(RestartCause::powerPolicyOn))
560 {
561 restartCause = getRestartCause(RestartCause::powerPolicyOn);
562 }
563 else if (causeSet.contains(RestartCause::powerPolicyRestore))
564 {
565 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
566 }
567 else if (causeSet.contains(RestartCause::softReset))
568 {
569 restartCause = getRestartCause(RestartCause::softReset);
570 }
571
572 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700573}
574
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700575static void systemPowerGoodFailedLog()
576{
577 sd_journal_send(
578 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
579 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
580 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
581 sioPowerGoodWatchdogTimeMs, NULL);
582}
583
584static void psPowerOKFailedLog()
585{
586 sd_journal_send(
587 "MESSAGE=PowerControl: power supply power good failed to assert",
588 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
589 "OpenBMC.0.1.PowerSupplyPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
590 psPowerOKWatchdogTimeMs, NULL);
591}
592
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700593static void powerRestorePolicyLog()
594{
595 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
596 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
597 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
598}
599
600static void powerButtonPressLog()
601{
602 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
603 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
604 "OpenBMC.0.1.PowerButtonPressed", NULL);
605}
606
607static void resetButtonPressLog()
608{
609 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
610 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
611 "OpenBMC.0.1.ResetButtonPressed", NULL);
612}
613
614static void nmiButtonPressLog()
615{
616 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
617 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
618 "OpenBMC.0.1.NMIButtonPressed", NULL);
619}
620
621static void nmiDiagIntLog()
622{
623 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
624 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
625 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
626}
627
628static int initializePowerStateStorage()
629{
630 // create the power control directory if it doesn't exist
631 std::error_code ec;
632 if (!(std::filesystem::create_directories(powerControlDir, ec)))
633 {
634 if (ec.value() != 0)
635 {
636 std::cerr << "failed to create " << powerControlDir << ": "
637 << ec.message() << "\n";
638 return -1;
639 }
640 }
641 // Create the power state file if it doesn't exist
642 if (!std::filesystem::exists(powerControlDir / powerStateFile))
643 {
644 std::ofstream powerStateStream(powerControlDir / powerStateFile);
645 powerStateStream << getChassisState(powerState);
646 }
647 return 0;
648}
649
650static bool wasPowerDropped()
651{
652 std::ifstream powerStateStream(powerControlDir / powerStateFile);
653 if (!powerStateStream.is_open())
654 {
655 std::cerr << "Failed to open power state file\n";
656 return false;
657 }
658
659 std::string state;
660 std::getline(powerStateStream, state);
661 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
662}
663
664static void invokePowerRestorePolicy(const std::string& policy)
665{
666 // Async events may call this twice, but we only want to run once
667 static bool policyInvoked = false;
668 if (policyInvoked)
669 {
670 return;
671 }
672 policyInvoked = true;
673
674 std::cerr << "Power restore delay expired, invoking " << policy << "\n";
675 if (policy ==
676 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
677 {
678 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700679 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700680 }
681 else if (policy == "xyz.openbmc_project.Control.Power.RestorePolicy."
682 "Policy.Restore")
683 {
684 if (wasPowerDropped())
685 {
686 std::cerr << "Power was dropped, restoring Host On state\n";
687 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700688 setRestartCauseProperty(
689 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700690 }
691 else
692 {
693 std::cerr << "No power drop, restoring Host Off state\n";
694 }
695 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -0700696 // We're done with the previous power state for the restore policy, so store
697 // the current state
698 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700699}
700
701static void powerRestorePolicyDelay(int delay)
702{
703 // Async events may call this twice, but we only want to run once
704 static bool delayStarted = false;
705 if (delayStarted)
706 {
707 return;
708 }
709 delayStarted = true;
710 // Calculate the delay from now to meet the requested delay
711 // Subtract the approximate uboot time
712 static constexpr const int ubootSeconds = 20;
713 delay -= ubootSeconds;
714 // Subtract the time since boot
715 struct sysinfo info = {};
716 if (sysinfo(&info) == 0)
717 {
718 delay -= info.uptime;
719 }
720 // 0 is the minimum delay
721 delay = std::max(delay, 0);
722
723 static boost::asio::steady_timer powerRestorePolicyTimer(io);
724 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
725 std::cerr << "Power restore delay of " << delay << " seconds started\n";
726 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
727 if (ec)
728 {
729 // operation_aborted is expected if timer is canceled before
730 // completion.
731 if (ec != boost::asio::error::operation_aborted)
732 {
733 std::cerr << "power restore policy async_wait failed: "
734 << ec.message() << "\n";
735 }
736 return;
737 }
738 // Get Power Restore Policy
739 // In case PowerRestorePolicy is not available, set a match for it
740 static std::unique_ptr<sdbusplus::bus::match::match>
741 powerRestorePolicyMatch = std::make_unique<
742 sdbusplus::bus::match::match>(
743 *conn,
744 "type='signal',interface='org.freedesktop.DBus.Properties',"
745 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
746 "project.Control.Power.RestorePolicy'",
747 [](sdbusplus::message::message& msg) {
748 std::string interfaceName;
749 boost::container::flat_map<std::string,
750 std::variant<std::string>>
751 propertiesChanged;
752 std::string policy;
753 try
754 {
755 msg.read(interfaceName, propertiesChanged);
756 policy = std::get<std::string>(
757 propertiesChanged.begin()->second);
758 }
759 catch (std::exception& e)
760 {
761 std::cerr
762 << "Unable to read power restore policy value\n";
763 powerRestorePolicyMatch.reset();
764 return;
765 }
766 invokePowerRestorePolicy(policy);
767 powerRestorePolicyMatch.reset();
768 });
769
770 // Check if it's already on DBus
771 conn->async_method_call(
772 [](boost::system::error_code ec,
773 const std::variant<std::string>& policyProperty) {
774 if (ec)
775 {
776 return;
777 }
778 powerRestorePolicyMatch.reset();
779 const std::string* policy =
780 std::get_if<std::string>(&policyProperty);
781 if (policy == nullptr)
782 {
783 std::cerr << "Unable to read power restore policy value\n";
784 return;
785 }
786 invokePowerRestorePolicy(*policy);
787 },
788 "xyz.openbmc_project.Settings",
789 "/xyz/openbmc_project/control/host0/power_restore_policy",
790 "org.freedesktop.DBus.Properties", "Get",
791 "xyz.openbmc_project.Control.Power.RestorePolicy",
792 "PowerRestorePolicy");
793 });
794}
795
796static void powerRestorePolicyStart()
797{
798 std::cerr << "Power restore policy started\n";
799 powerRestorePolicyLog();
800
801 // Get the desired delay time
802 // In case PowerRestoreDelay is not available, set a match for it
803 static std::unique_ptr<sdbusplus::bus::match::match>
804 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
805 *conn,
806 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
807 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
808 "Power.RestoreDelay'",
809 [](sdbusplus::message::message& msg) {
810 std::string interfaceName;
811 boost::container::flat_map<std::string, std::variant<uint16_t>>
812 propertiesChanged;
813 int delay = 0;
814 try
815 {
816 msg.read(interfaceName, propertiesChanged);
817 delay =
818 std::get<uint16_t>(propertiesChanged.begin()->second);
819 }
820 catch (std::exception& e)
821 {
822 std::cerr << "Unable to read power restore delay value\n";
823 powerRestoreDelayMatch.reset();
824 return;
825 }
826 powerRestorePolicyDelay(delay);
827 powerRestoreDelayMatch.reset();
828 });
829
830 // Check if it's already on DBus
831 conn->async_method_call(
832 [](boost::system::error_code ec,
833 const std::variant<uint16_t>& delayProperty) {
834 if (ec)
835 {
836 return;
837 }
838 powerRestoreDelayMatch.reset();
839 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
840 if (delay == nullptr)
841 {
842 std::cerr << "Unable to read power restore delay value\n";
843 return;
844 }
845 powerRestorePolicyDelay(*delay);
846 },
847 "xyz.openbmc_project.Settings",
848 "/xyz/openbmc_project/control/power_restore_delay",
849 "org.freedesktop.DBus.Properties", "Get",
850 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
851}
852
853static void powerRestorePolicyCheck()
854{
855 // In case ACBoot is not available, set a match for it
856 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
857 std::make_unique<sdbusplus::bus::match::match>(
858 *conn,
859 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
860 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
861 "ACBoot'",
862 [](sdbusplus::message::message& msg) {
863 std::string interfaceName;
864 boost::container::flat_map<std::string,
865 std::variant<std::string>>
866 propertiesChanged;
867 std::string acBoot;
868 try
869 {
870 msg.read(interfaceName, propertiesChanged);
871 acBoot = std::get<std::string>(
872 propertiesChanged.begin()->second);
873 }
874 catch (std::exception& e)
875 {
876 std::cerr << "Unable to read AC Boot status\n";
877 acBootMatch.reset();
878 return;
879 }
880 if (acBoot == "Unknown")
881 {
882 return;
883 }
884 if (acBoot == "True")
885 {
886 // Start the Power Restore policy
887 powerRestorePolicyStart();
888 }
889 acBootMatch.reset();
890 });
891
892 // Check if it's already on DBus
893 conn->async_method_call(
894 [](boost::system::error_code ec,
895 const std::variant<std::string>& acBootProperty) {
896 if (ec)
897 {
898 return;
899 }
900 const std::string* acBoot =
901 std::get_if<std::string>(&acBootProperty);
902 if (acBoot == nullptr)
903 {
904 std::cerr << "Unable to read AC Boot status\n";
905 return;
906 }
907 if (*acBoot == "Unknown")
908 {
909 return;
910 }
911 if (*acBoot == "True")
912 {
913 // Start the Power Restore policy
914 powerRestorePolicyStart();
915 }
916 acBootMatch.reset();
917 },
918 "xyz.openbmc_project.Settings",
919 "/xyz/openbmc_project/control/host0/ac_boot",
920 "org.freedesktop.DBus.Properties", "Get",
921 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
922}
923
924static bool requestGPIOEvents(
925 const std::string& name, const std::function<void()>& handler,
926 gpiod::line& gpioLine,
927 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
928{
929 // Find the GPIO line
930 gpioLine = gpiod::find_line(name);
931 if (!gpioLine)
932 {
933 std::cerr << "Failed to find the " << name << " line\n";
934 return false;
935 }
936
937 try
938 {
939 gpioLine.request(
940 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
941 }
942 catch (std::exception&)
943 {
944 std::cerr << "Failed to request events for " << name << "\n";
945 return false;
946 }
947
948 int gpioLineFd = gpioLine.event_get_fd();
949 if (gpioLineFd < 0)
950 {
951 std::cerr << "Failed to get " << name << " fd\n";
952 return false;
953 }
954
955 gpioEventDescriptor.assign(gpioLineFd);
956
957 gpioEventDescriptor.async_wait(
958 boost::asio::posix::stream_descriptor::wait_read,
959 [&name, handler](const boost::system::error_code ec) {
960 if (ec)
961 {
962 std::cerr << name << " fd handler error: " << ec.message()
963 << "\n";
964 // TODO: throw here to force power-control to restart?
965 return;
966 }
967 handler();
968 });
969 return true;
970}
971
972static bool setGPIOOutput(const std::string& name, const int value,
973 gpiod::line& gpioLine)
974{
975 // Find the GPIO line
976 gpioLine = gpiod::find_line(name);
977 if (!gpioLine)
978 {
979 std::cerr << "Failed to find the " << name << " line.\n";
980 return false;
981 }
982
983 // Request GPIO output to specified value
984 try
985 {
986 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
987 value);
988 }
989 catch (std::exception&)
990 {
991 std::cerr << "Failed to request " << name << " output\n";
992 return false;
993 }
994
995 std::cerr << name << " set to " << std::to_string(value) << "\n";
996 return true;
997}
998
999static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1000 const std::string& name, const int value,
1001 const int durationMs)
1002{
1003 // Set the masked GPIO line to the specified value
1004 maskedGPIOLine.set_value(value);
1005 std::cerr << name << " set to " << std::to_string(value) << "\n";
1006 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1007 gpioAssertTimer.async_wait(
1008 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
1009 // Set the masked GPIO line back to the opposite value
1010 maskedGPIOLine.set_value(!value);
1011 std::cerr << name << " released\n";
1012 if (ec)
1013 {
1014 // operation_aborted is expected if timer is canceled before
1015 // completion.
1016 if (ec != boost::asio::error::operation_aborted)
1017 {
1018 std::cerr << name << " async_wait failed: " + ec.message()
1019 << "\n";
1020 }
1021 }
1022 });
1023 return 0;
1024}
1025
1026static int setGPIOOutputForMs(const std::string& name, const int value,
1027 const int durationMs)
1028{
1029 // If the requested GPIO is masked, use the mask line to set the output
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001030 if (powerButtonMask && name == power_control::powerOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001031 {
1032 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
1033 durationMs);
1034 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001035 if (resetButtonMask && name == power_control::resetOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001036 {
1037 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
1038 durationMs);
1039 }
1040
1041 // No mask set, so request and set the GPIO normally
1042 gpiod::line gpioLine;
1043 if (!setGPIOOutput(name, value, gpioLine))
1044 {
1045 return -1;
1046 }
1047 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1048 gpioAssertTimer.async_wait(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001049 [gpioLine, value, name](const boost::system::error_code ec) {
1050 // Set the GPIO line back to the opposite value
1051 gpioLine.set_value(!value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001052 std::cerr << name << " released\n";
1053 if (ec)
1054 {
1055 // operation_aborted is expected if timer is canceled before
1056 // completion.
1057 if (ec != boost::asio::error::operation_aborted)
1058 {
1059 std::cerr << name << " async_wait failed: " << ec.message()
1060 << "\n";
1061 }
1062 }
1063 });
1064 return 0;
1065}
1066
1067static void powerOn()
1068{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001069 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001070}
1071
1072static void gracefulPowerOff()
1073{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001074 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001075}
1076
1077static void forcePowerOff()
1078{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001079 if (setGPIOOutputForMs(power_control::powerOutName, 0,
1080 forceOffPulseTimeMs) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001081 {
1082 return;
1083 }
1084
1085 // If the force off timer expires, then the PCH power-button override
1086 // failed, so attempt the Unconditional Powerdown SMBus command.
1087 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1088 if (ec)
1089 {
1090 // operation_aborted is expected if timer is canceled before
1091 // completion.
1092 if (ec != boost::asio::error::operation_aborted)
1093 {
1094 std::cerr << "Force power off async_wait failed: "
1095 << ec.message() << "\n";
1096 }
1097 return;
1098 }
1099 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
1100 "Powerdown SMBus command.\n";
1101 const static constexpr size_t pchDevBusAddress = 3;
1102 const static constexpr size_t pchDevSlaveAddress = 0x44;
1103 const static constexpr size_t pchCmdReg = 0;
1104 const static constexpr size_t pchPowerDownCmd = 0x02;
1105 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
1106 pchPowerDownCmd) < 0)
1107 {
1108 std::cerr << "Unconditional Powerdown command failed! Not sure "
1109 "what to do now.\n";
1110 }
1111 });
1112}
1113
1114static void reset()
1115{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001116 setGPIOOutputForMs(power_control::resetOutName, 0, resetPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001117}
1118
1119static void gracefulPowerOffTimerStart()
1120{
1121 std::cerr << "Graceful power-off timer started\n";
1122 gracefulPowerOffTimer.expires_after(
1123 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1124 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1125 if (ec)
1126 {
1127 // operation_aborted is expected if timer is canceled before
1128 // completion.
1129 if (ec != boost::asio::error::operation_aborted)
1130 {
1131 std::cerr << "Graceful power-off async_wait failed: "
1132 << ec.message() << "\n";
1133 }
1134 std::cerr << "Graceful power-off timer canceled\n";
1135 return;
1136 }
1137 std::cerr << "Graceful power-off timer completed\n";
1138 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1139 });
1140}
1141
1142static void powerCycleTimerStart()
1143{
1144 std::cerr << "Power-cycle timer started\n";
1145 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1146 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1147 if (ec)
1148 {
1149 // operation_aborted is expected if timer is canceled before
1150 // completion.
1151 if (ec != boost::asio::error::operation_aborted)
1152 {
1153 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1154 << "\n";
1155 }
1156 std::cerr << "Power-cycle timer canceled\n";
1157 return;
1158 }
1159 std::cerr << "Power-cycle timer completed\n";
1160 sendPowerControlEvent(Event::powerCycleTimerExpired);
1161 });
1162}
1163
1164static void psPowerOKWatchdogTimerStart()
1165{
1166 std::cerr << "power supply power OK watchdog timer started\n";
1167 psPowerOKWatchdogTimer.expires_after(
1168 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1169 psPowerOKWatchdogTimer.async_wait(
1170 [](const boost::system::error_code ec) {
1171 if (ec)
1172 {
1173 // operation_aborted is expected if timer is canceled before
1174 // completion.
1175 if (ec != boost::asio::error::operation_aborted)
1176 {
1177 std::cerr
1178 << "power supply power OK watchdog async_wait failed: "
1179 << ec.message() << "\n";
1180 }
1181 std::cerr << "power supply power OK watchdog timer canceled\n";
1182 return;
1183 }
1184 std::cerr << "power supply power OK watchdog timer expired\n";
1185 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1186 });
1187}
1188
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001189static void warmResetCheckTimerStart()
1190{
1191 std::cerr << "Warm reset check timer started\n";
1192 warmResetCheckTimer.expires_after(
1193 std::chrono::milliseconds(warmResetCheckTimeMs));
1194 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1195 if (ec)
1196 {
1197 // operation_aborted is expected if timer is canceled before
1198 // completion.
1199 if (ec != boost::asio::error::operation_aborted)
1200 {
1201 std::cerr << "Warm reset check async_wait failed: "
1202 << ec.message() << "\n";
1203 }
1204 std::cerr << "Warm reset check timer canceled\n";
1205 return;
1206 }
1207 std::cerr << "Warm reset check timer completed\n";
1208 sendPowerControlEvent(Event::warmResetDetected);
1209 });
1210}
1211
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001212static void pohCounterTimerStart()
1213{
1214 std::cerr << "POH timer started\n";
1215 // Set the time-out as 1 hour, to align with POH command in ipmid
1216 pohCounterTimer.expires_after(std::chrono::hours(1));
1217 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1218 if (ec)
1219 {
1220 // operation_aborted is expected if timer is canceled before
1221 // completion.
1222 if (ec != boost::asio::error::operation_aborted)
1223 {
1224 std::cerr << "POH timer async_wait failed: " << ec.message()
1225 << "\n";
1226 }
1227 std::cerr << "POH timer canceled\n";
1228 return;
1229 }
1230
1231 if (getHostState(powerState) !=
1232 "xyz.openbmc_project.State.Host.HostState.Running")
1233 {
1234 return;
1235 }
1236
1237 conn->async_method_call(
1238 [](boost::system::error_code ec,
1239 const std::variant<uint32_t>& pohCounterProperty) {
1240 if (ec)
1241 {
1242 std::cerr << "error to get poh counter\n";
1243 return;
1244 }
1245 const uint32_t* pohCounter =
1246 std::get_if<uint32_t>(&pohCounterProperty);
1247 if (pohCounter == nullptr)
1248 {
1249 std::cerr << "unable to read poh counter\n";
1250 return;
1251 }
1252
1253 conn->async_method_call(
1254 [](boost::system::error_code ec) {
1255 if (ec)
1256 {
1257 std::cerr << "failed to set poh counter\n";
1258 }
1259 },
1260 "xyz.openbmc_project.Settings",
1261 "/xyz/openbmc_project/state/chassis0",
1262 "org.freedesktop.DBus.Properties", "Set",
1263 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1264 std::variant<uint32_t>(*pohCounter + 1));
1265 },
1266 "xyz.openbmc_project.Settings",
1267 "/xyz/openbmc_project/state/chassis0",
1268 "org.freedesktop.DBus.Properties", "Get",
1269 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1270
1271 pohCounterTimerStart();
1272 });
1273}
1274
1275static void currentHostStateMonitor()
1276{
Yong Li8d660212019-12-27 10:18:10 +08001277 if (getHostState(powerState) ==
1278 "xyz.openbmc_project.State.Host.HostState.Running")
1279 {
1280 pohCounterTimerStart();
1281 // Clear the restart cause set for the next restart
1282 clearRestartCause();
1283 }
1284 else
1285 {
1286 pohCounterTimer.cancel();
1287 // Set the restart cause set for this restart
1288 setRestartCause();
1289 }
1290
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001291 static auto match = sdbusplus::bus::match::match(
1292 *conn,
1293 "type='signal',member='PropertiesChanged', "
1294 "interface='org.freedesktop.DBus.Properties', "
1295 "arg0namespace='xyz.openbmc_project.State.Host'",
1296 [](sdbusplus::message::message& message) {
1297 std::string intfName;
1298 std::map<std::string, std::variant<std::string>> properties;
1299
1300 message.read(intfName, properties);
1301
1302 std::variant<std::string> currentHostState;
1303
1304 try
1305 {
1306 currentHostState = properties.at("CurrentHostState");
1307 }
1308 catch (const std::out_of_range& e)
1309 {
1310 std::cerr << "Error in finding CurrentHostState property\n";
1311
1312 return;
1313 }
1314
1315 if (std::get<std::string>(currentHostState) ==
1316 "xyz.openbmc_project.State.Host.HostState.Running")
1317 {
1318 pohCounterTimerStart();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001319 // Clear the restart cause set for the next restart
1320 clearRestartCause();
Yong Li8d660212019-12-27 10:18:10 +08001321 sd_journal_send("MESSAGE=Host system DC power is on",
1322 "PRIORITY=%i", LOG_INFO,
1323 "REDFISH_MESSAGE_ID=%s",
1324 "OpenBMC.0.1.DCPowerOn", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001325 }
1326 else
1327 {
1328 pohCounterTimer.cancel();
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301329 // POST_COMPLETE GPIO event is not working in some platforms
1330 // when power state is changed to OFF. This resulted in
1331 // 'OperatingSystemState' to stay at 'Standby', even though
1332 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1333 // if HostState is trurned to OFF.
1334 osIface->set_property("OperatingSystemState",
1335 std::string("Inactive"));
1336
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001337 // Set the restart cause set for this restart
1338 setRestartCause();
Rashmi RV89f61312020-01-22 15:41:50 +05301339 resetACBootProperty();
Yong Li8d660212019-12-27 10:18:10 +08001340 sd_journal_send("MESSAGE=Host system DC power is off",
1341 "PRIORITY=%i", LOG_INFO,
1342 "REDFISH_MESSAGE_ID=%s",
1343 "OpenBMC.0.1.DCPowerOff", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001344 }
1345 });
1346}
1347
1348static void sioPowerGoodWatchdogTimerStart()
1349{
1350 std::cerr << "SIO power good watchdog timer started\n";
1351 sioPowerGoodWatchdogTimer.expires_after(
1352 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1353 sioPowerGoodWatchdogTimer.async_wait(
1354 [](const boost::system::error_code ec) {
1355 if (ec)
1356 {
1357 // operation_aborted is expected if timer is canceled before
1358 // completion.
1359 if (ec != boost::asio::error::operation_aborted)
1360 {
1361 std::cerr << "SIO power good watchdog async_wait failed: "
1362 << ec.message() << "\n";
1363 }
1364 std::cerr << "SIO power good watchdog timer canceled\n";
1365 return;
1366 }
1367 std::cerr << "SIO power good watchdog timer completed\n";
1368 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1369 });
1370}
1371
1372static void powerStateOn(const Event event)
1373{
1374 logEvent(__FUNCTION__, event);
1375 switch (event)
1376 {
1377 case Event::psPowerOKDeAssert:
1378 setPowerState(PowerState::off);
1379 // DC power is unexpectedly lost, beep
1380 beep(beepPowerFail);
1381 break;
1382 case Event::sioS5Assert:
1383 setPowerState(PowerState::transitionToOff);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001384 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001385 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001386 case Event::postCompleteDeAssert:
1387 setPowerState(PowerState::checkForWarmReset);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001388 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001389 warmResetCheckTimerStart();
1390 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001391 case Event::powerButtonPressed:
1392 setPowerState(PowerState::gracefulTransitionToOff);
1393 gracefulPowerOffTimerStart();
1394 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001395 case Event::resetButtonPressed:
1396 setPowerState(PowerState::checkForWarmReset);
1397 warmResetCheckTimerStart();
1398 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001399 case Event::powerOffRequest:
1400 setPowerState(PowerState::transitionToOff);
1401 forcePowerOff();
1402 break;
1403 case Event::gracefulPowerOffRequest:
1404 setPowerState(PowerState::gracefulTransitionToOff);
1405 gracefulPowerOffTimerStart();
1406 gracefulPowerOff();
1407 break;
1408 case Event::powerCycleRequest:
1409 setPowerState(PowerState::transitionToCycleOff);
1410 forcePowerOff();
1411 break;
1412 case Event::gracefulPowerCycleRequest:
1413 setPowerState(PowerState::gracefulTransitionToCycleOff);
1414 gracefulPowerOffTimerStart();
1415 gracefulPowerOff();
1416 break;
1417 case Event::resetRequest:
1418 reset();
1419 break;
1420 default:
1421 std::cerr << "No action taken.\n";
1422 break;
1423 }
1424}
1425
1426static void powerStateWaitForPSPowerOK(const Event event)
1427{
1428 logEvent(__FUNCTION__, event);
1429 switch (event)
1430 {
1431 case Event::psPowerOKAssert:
1432 // Cancel any GPIO assertions held during the transition
1433 gpioAssertTimer.cancel();
1434 psPowerOKWatchdogTimer.cancel();
1435 sioPowerGoodWatchdogTimerStart();
1436 setPowerState(PowerState::waitForSIOPowerGood);
1437 break;
1438 case Event::psPowerOKWatchdogTimerExpired:
1439 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001440 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001441 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001442 case Event::sioPowerGoodAssert:
1443 psPowerOKWatchdogTimer.cancel();
1444 setPowerState(PowerState::on);
1445 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001446 default:
1447 std::cerr << "No action taken.\n";
1448 break;
1449 }
1450}
1451
1452static void powerStateWaitForSIOPowerGood(const Event event)
1453{
1454 logEvent(__FUNCTION__, event);
1455 switch (event)
1456 {
1457 case Event::sioPowerGoodAssert:
1458 sioPowerGoodWatchdogTimer.cancel();
1459 setPowerState(PowerState::on);
1460 break;
1461 case Event::sioPowerGoodWatchdogTimerExpired:
1462 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001463 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001464 forcePowerOff();
1465 break;
1466 default:
1467 std::cerr << "No action taken.\n";
1468 break;
1469 }
1470}
1471
1472static void powerStateFailedTransitionToOn(const Event event)
1473{
1474 logEvent(__FUNCTION__, event);
1475 switch (event)
1476 {
1477 case Event::psPowerOKAssert:
1478 // We're in a failure state, so don't allow the system to turn on
1479 // without a user request
1480 forcePowerOff();
1481 break;
1482 case Event::psPowerOKDeAssert:
1483 // Cancel any GPIO assertions held during the transition
1484 gpioAssertTimer.cancel();
1485 break;
1486 case Event::powerButtonPressed:
1487 psPowerOKWatchdogTimerStart();
1488 setPowerState(PowerState::waitForPSPowerOK);
1489 break;
1490 case Event::powerOnRequest:
1491 psPowerOKWatchdogTimerStart();
1492 setPowerState(PowerState::waitForPSPowerOK);
1493 powerOn();
1494 break;
1495 default:
1496 std::cerr << "No action taken.\n";
1497 break;
1498 }
1499}
1500
1501static void powerStateOff(const Event event)
1502{
1503 logEvent(__FUNCTION__, event);
1504 switch (event)
1505 {
1506 case Event::psPowerOKAssert:
1507 setPowerState(PowerState::waitForSIOPowerGood);
1508 break;
1509 case Event::sioS5DeAssert:
1510 setPowerState(PowerState::waitForPSPowerOK);
1511 break;
1512 case Event::powerButtonPressed:
1513 psPowerOKWatchdogTimerStart();
1514 setPowerState(PowerState::waitForPSPowerOK);
1515 break;
1516 case Event::powerOnRequest:
1517 psPowerOKWatchdogTimerStart();
1518 setPowerState(PowerState::waitForPSPowerOK);
1519 powerOn();
1520 break;
1521 default:
1522 std::cerr << "No action taken.\n";
1523 break;
1524 }
1525}
1526
1527static void powerStateTransitionToOff(const Event event)
1528{
1529 logEvent(__FUNCTION__, event);
1530 switch (event)
1531 {
1532 case Event::psPowerOKDeAssert:
1533 // Cancel any GPIO assertions held during the transition
1534 gpioAssertTimer.cancel();
1535 setPowerState(PowerState::off);
1536 break;
1537 default:
1538 std::cerr << "No action taken.\n";
1539 break;
1540 }
1541}
1542
1543static void powerStateGracefulTransitionToOff(const Event event)
1544{
1545 logEvent(__FUNCTION__, event);
1546 switch (event)
1547 {
1548 case Event::psPowerOKDeAssert:
1549 gracefulPowerOffTimer.cancel();
1550 setPowerState(PowerState::off);
1551 break;
1552 case Event::gracefulPowerOffTimerExpired:
1553 setPowerState(PowerState::on);
1554 break;
1555 default:
1556 std::cerr << "No action taken.\n";
1557 break;
1558 }
1559}
1560
1561static void powerStateCycleOff(const Event event)
1562{
1563 logEvent(__FUNCTION__, event);
1564 switch (event)
1565 {
1566 case Event::powerCycleTimerExpired:
1567 psPowerOKWatchdogTimerStart();
1568 setPowerState(PowerState::waitForPSPowerOK);
1569 powerOn();
1570 break;
1571 default:
1572 std::cerr << "No action taken.\n";
1573 break;
1574 }
1575}
1576
1577static void powerStateTransitionToCycleOff(const Event event)
1578{
1579 logEvent(__FUNCTION__, event);
1580 switch (event)
1581 {
1582 case Event::psPowerOKDeAssert:
1583 // Cancel any GPIO assertions held during the transition
1584 gpioAssertTimer.cancel();
1585 setPowerState(PowerState::cycleOff);
1586 powerCycleTimerStart();
1587 break;
1588 default:
1589 std::cerr << "No action taken.\n";
1590 break;
1591 }
1592}
1593
1594static void powerStateGracefulTransitionToCycleOff(const Event event)
1595{
1596 logEvent(__FUNCTION__, event);
1597 switch (event)
1598 {
1599 case Event::psPowerOKDeAssert:
1600 gracefulPowerOffTimer.cancel();
1601 setPowerState(PowerState::cycleOff);
1602 powerCycleTimerStart();
1603 break;
1604 case Event::gracefulPowerOffTimerExpired:
1605 setPowerState(PowerState::on);
1606 break;
1607 default:
1608 std::cerr << "No action taken.\n";
1609 break;
1610 }
1611}
1612
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001613static void powerStateCheckForWarmReset(const Event event)
1614{
1615 logEvent(__FUNCTION__, event);
1616 switch (event)
1617 {
1618 case Event::sioS5Assert:
1619 warmResetCheckTimer.cancel();
1620 setPowerState(PowerState::transitionToOff);
1621 break;
1622 case Event::warmResetDetected:
1623 setPowerState(PowerState::on);
1624 break;
P.K. Lee344dae82019-11-27 16:35:05 +08001625 case Event::psPowerOKDeAssert:
1626 warmResetCheckTimer.cancel();
1627 setPowerState(PowerState::off);
1628 // DC power is unexpectedly lost, beep
1629 beep(beepPowerFail);
1630 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001631 default:
1632 std::cerr << "No action taken.\n";
1633 break;
1634 }
1635}
1636
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001637static void psPowerOKHandler()
1638{
1639 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1640
1641 Event powerControlEvent =
1642 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1643 ? Event::psPowerOKAssert
1644 : Event::psPowerOKDeAssert;
1645
1646 sendPowerControlEvent(powerControlEvent);
1647 psPowerOKEvent.async_wait(
1648 boost::asio::posix::stream_descriptor::wait_read,
1649 [](const boost::system::error_code ec) {
1650 if (ec)
1651 {
1652 std::cerr << "power supply power OK handler error: "
1653 << ec.message() << "\n";
1654 return;
1655 }
1656 psPowerOKHandler();
1657 });
1658}
1659
1660static void sioPowerGoodHandler()
1661{
1662 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1663
1664 Event powerControlEvent =
1665 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1666 ? Event::sioPowerGoodAssert
1667 : Event::sioPowerGoodDeAssert;
1668
1669 sendPowerControlEvent(powerControlEvent);
1670 sioPowerGoodEvent.async_wait(
1671 boost::asio::posix::stream_descriptor::wait_read,
1672 [](const boost::system::error_code ec) {
1673 if (ec)
1674 {
1675 std::cerr << "SIO power good handler error: " << ec.message()
1676 << "\n";
1677 return;
1678 }
1679 sioPowerGoodHandler();
1680 });
1681}
1682
1683static void sioOnControlHandler()
1684{
1685 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1686
1687 bool sioOnControl =
1688 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1689 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1690 sioOnControlEvent.async_wait(
1691 boost::asio::posix::stream_descriptor::wait_read,
1692 [](const boost::system::error_code ec) {
1693 if (ec)
1694 {
1695 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1696 << "\n";
1697 return;
1698 }
1699 sioOnControlHandler();
1700 });
1701}
1702
1703static void sioS5Handler()
1704{
1705 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1706
1707 Event powerControlEvent =
1708 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1709 ? Event::sioS5Assert
1710 : Event::sioS5DeAssert;
1711
1712 sendPowerControlEvent(powerControlEvent);
1713 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1714 [](const boost::system::error_code ec) {
1715 if (ec)
1716 {
1717 std::cerr << "SIO S5 handler error: "
1718 << ec.message() << "\n";
1719 return;
1720 }
1721 sioS5Handler();
1722 });
1723}
1724
1725static void powerButtonHandler()
1726{
1727 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1728
1729 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1730 {
1731 powerButtonPressLog();
1732 powerButtonIface->set_property("ButtonPressed", true);
1733 if (!powerButtonMask)
1734 {
1735 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001736 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001737 }
1738 else
1739 {
1740 std::cerr << "power button press masked\n";
1741 }
1742 }
1743 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1744 {
1745 powerButtonIface->set_property("ButtonPressed", false);
1746 }
1747 powerButtonEvent.async_wait(
1748 boost::asio::posix::stream_descriptor::wait_read,
1749 [](const boost::system::error_code ec) {
1750 if (ec)
1751 {
1752 std::cerr << "power button handler error: " << ec.message()
1753 << "\n";
1754 return;
1755 }
1756 powerButtonHandler();
1757 });
1758}
1759
1760static void resetButtonHandler()
1761{
1762 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1763
1764 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1765 {
1766 resetButtonPressLog();
1767 resetButtonIface->set_property("ButtonPressed", true);
1768 if (!resetButtonMask)
1769 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001770 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001771 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001772 }
1773 else
1774 {
1775 std::cerr << "reset button press masked\n";
1776 }
1777 }
1778 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1779 {
1780 resetButtonIface->set_property("ButtonPressed", false);
1781 }
1782 resetButtonEvent.async_wait(
1783 boost::asio::posix::stream_descriptor::wait_read,
1784 [](const boost::system::error_code ec) {
1785 if (ec)
1786 {
1787 std::cerr << "reset button handler error: " << ec.message()
1788 << "\n";
1789 return;
1790 }
1791 resetButtonHandler();
1792 });
1793}
1794
1795static void nmiSetEnablePorperty(bool value)
1796{
1797 conn->async_method_call(
1798 [](boost::system::error_code ec) {
1799 if (ec)
1800 {
1801 std::cerr << "failed to set NMI source\n";
1802 }
1803 },
Chen Yugang303bd582019-11-01 08:45:06 +08001804 "xyz.openbmc_project.Settings",
1805 "/xyz/openbmc_project/Chassis/Control/NMISource",
1806 "org.freedesktop.DBus.Properties", "Set",
1807 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
1808 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001809}
1810
1811static void nmiReset(void)
1812{
1813 static constexpr const uint8_t value = 1;
1814 const static constexpr int nmiOutPulseTimeMs = 200;
1815
1816 std::cerr << "NMI out action \n";
1817 nmiOutLine.set_value(value);
1818 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1819 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1820 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1821 // restore the NMI_OUT GPIO line back to the opposite value
1822 nmiOutLine.set_value(!value);
1823 std::cerr << nmiOutName << " released\n";
1824 if (ec)
1825 {
1826 // operation_aborted is expected if timer is canceled before
1827 // completion.
1828 if (ec != boost::asio::error::operation_aborted)
1829 {
1830 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1831 << "\n";
1832 }
1833 }
1834 });
1835 // log to redfish
1836 nmiDiagIntLog();
1837 std::cerr << "NMI out action completed\n";
1838 // reset Enable Property
1839 nmiSetEnablePorperty(false);
1840}
1841
1842static void nmiSourcePropertyMonitor(void)
1843{
1844 std::cerr << " NMI Source Property Monitor \n";
1845
1846 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1847 std::make_unique<sdbusplus::bus::match::match>(
1848 *conn,
1849 "type='signal',interface='org.freedesktop.DBus.Properties',"
Chen Yugang303bd582019-11-01 08:45:06 +08001850 "member='PropertiesChanged',arg0namespace='xyz.openbmc_project."
1851 "Chassis.Control."
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001852 "NMISource'",
1853 [](sdbusplus::message::message& msg) {
1854 std::string interfaceName;
1855 boost::container::flat_map<std::string,
1856 std::variant<bool, std::string>>
1857 propertiesChanged;
1858 std::string state;
1859 bool value = true;
1860 try
1861 {
1862 msg.read(interfaceName, propertiesChanged);
1863 if (propertiesChanged.begin()->first == "Enabled")
1864 {
1865 value =
1866 std::get<bool>(propertiesChanged.begin()->second);
1867 std::cerr
1868 << " NMI Enabled propertiesChanged value: " << value
1869 << "\n";
1870 nmiEnabled = value;
1871 if (nmiEnabled)
1872 {
1873 nmiReset();
1874 }
1875 }
1876 }
1877 catch (std::exception& e)
1878 {
1879 std::cerr << "Unable to read NMI source\n";
1880 return;
1881 }
1882 });
1883}
1884
1885static void setNmiSource()
1886{
1887 conn->async_method_call(
1888 [](boost::system::error_code ec) {
1889 if (ec)
1890 {
1891 std::cerr << "failed to set NMI source\n";
1892 }
1893 },
Chen Yugang303bd582019-11-01 08:45:06 +08001894 "xyz.openbmc_project.Settings",
1895 "/xyz/openbmc_project/Chassis/Control/NMISource",
1896 "org.freedesktop.DBus.Properties", "Set",
1897 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
1898 std::variant<std::string>{"xyz.openbmc_project.Chassis.Control."
1899 "NMISource.BMCSourceSignal.FpBtn"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001900 // set Enable Property
1901 nmiSetEnablePorperty(true);
1902}
1903
1904static void nmiButtonHandler()
1905{
1906 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1907
1908 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1909 {
1910 nmiButtonPressLog();
1911 nmiButtonIface->set_property("ButtonPressed", true);
1912 if (nmiButtonMasked)
1913 {
1914 std::cerr << "NMI button press masked\n";
1915 }
1916 else
1917 {
1918 setNmiSource();
1919 }
1920 }
1921 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1922 {
1923 nmiButtonIface->set_property("ButtonPressed", false);
1924 }
1925 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1926 [](const boost::system::error_code ec) {
1927 if (ec)
1928 {
1929 std::cerr << "NMI button handler error: "
1930 << ec.message() << "\n";
1931 return;
1932 }
1933 nmiButtonHandler();
1934 });
1935}
1936
1937static void idButtonHandler()
1938{
1939 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1940
1941 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1942 {
1943 idButtonIface->set_property("ButtonPressed", true);
1944 }
1945 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1946 {
1947 idButtonIface->set_property("ButtonPressed", false);
1948 }
1949 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1950 [](const boost::system::error_code& ec) {
1951 if (ec)
1952 {
1953 std::cerr << "ID button handler error: "
1954 << ec.message() << "\n";
1955 return;
1956 }
1957 idButtonHandler();
1958 });
1959}
1960
1961static void postCompleteHandler()
1962{
1963 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1964
1965 bool postComplete =
1966 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001967 if (postComplete)
1968 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001969 sendPowerControlEvent(Event::postCompleteAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001970 osIface->set_property("OperatingSystemState", std::string("Standby"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001971 }
1972 else
1973 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001974 sendPowerControlEvent(Event::postCompleteDeAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001975 osIface->set_property("OperatingSystemState", std::string("Inactive"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001976 }
1977 postCompleteEvent.async_wait(
1978 boost::asio::posix::stream_descriptor::wait_read,
1979 [](const boost::system::error_code ec) {
1980 if (ec)
1981 {
1982 std::cerr << "POST complete handler error: " << ec.message()
1983 << "\n";
1984 return;
1985 }
1986 postCompleteHandler();
1987 });
1988}
1989} // namespace power_control
1990
1991int main(int argc, char* argv[])
1992{
1993 std::cerr << "Start Chassis power control service...\n";
1994 power_control::conn =
1995 std::make_shared<sdbusplus::asio::connection>(power_control::io);
1996
1997 // Request all the dbus names
1998 power_control::conn->request_name("xyz.openbmc_project.State.Host");
1999 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
2000 power_control::conn->request_name(
2001 "xyz.openbmc_project.State.OperatingSystem");
2002 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
Chen Yugang174ec662019-08-19 19:58:49 +08002003 power_control::conn->request_name("xyz.openbmc_project.Control.Host.NMI");
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002004 power_control::conn->request_name(
2005 "xyz.openbmc_project.Control.Host.RestartCause");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002006
2007 // Request PS_PWROK GPIO events
2008 if (!power_control::requestGPIOEvents(
2009 "PS_PWROK", power_control::psPowerOKHandler,
2010 power_control::psPowerOKLine, power_control::psPowerOKEvent))
2011 {
2012 return -1;
2013 }
2014
2015 // Request SIO_POWER_GOOD GPIO events
2016 if (!power_control::requestGPIOEvents(
2017 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
2018 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
2019 {
2020 return -1;
2021 }
2022
2023 // Request SIO_ONCONTROL GPIO events
2024 if (!power_control::requestGPIOEvents(
2025 "SIO_ONCONTROL", power_control::sioOnControlHandler,
2026 power_control::sioOnControlLine, power_control::sioOnControlEvent))
2027 {
2028 return -1;
2029 }
2030
2031 // Request SIO_S5 GPIO events
2032 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
2033 power_control::sioS5Line,
2034 power_control::sioS5Event))
2035 {
2036 return -1;
2037 }
2038
2039 // Request POWER_BUTTON GPIO events
2040 if (!power_control::requestGPIOEvents(
2041 "POWER_BUTTON", power_control::powerButtonHandler,
2042 power_control::powerButtonLine, power_control::powerButtonEvent))
2043 {
2044 return -1;
2045 }
2046
2047 // Request RESET_BUTTON GPIO events
2048 if (!power_control::requestGPIOEvents(
2049 "RESET_BUTTON", power_control::resetButtonHandler,
2050 power_control::resetButtonLine, power_control::resetButtonEvent))
2051 {
2052 return -1;
2053 }
2054
2055 // Request NMI_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08002056 power_control::requestGPIOEvents(
2057 "NMI_BUTTON", power_control::nmiButtonHandler,
2058 power_control::nmiButtonLine, power_control::nmiButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002059
2060 // Request ID_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08002061 power_control::requestGPIOEvents(
2062 "ID_BUTTON", power_control::idButtonHandler,
2063 power_control::idButtonLine, power_control::idButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002064
2065 // Request POST_COMPLETE GPIO events
2066 if (!power_control::requestGPIOEvents(
2067 "POST_COMPLETE", power_control::postCompleteHandler,
2068 power_control::postCompleteLine, power_control::postCompleteEvent))
2069 {
2070 return -1;
2071 }
2072
2073 // initialize NMI_OUT GPIO.
Vijay Khemka33a532d2019-11-14 16:50:35 -08002074 power_control::setGPIOOutput(power_control::nmiOutName, 0,
2075 power_control::nmiOutLine);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002076
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002077 // Initialize POWER_OUT and RESET_OUT GPIO.
2078 gpiod::line line;
2079 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
2080 {
2081 return -1;
2082 }
2083
2084 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
2085 {
2086 return -1;
2087 }
2088
2089 // Release line
2090 line.reset();
2091
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002092 // Initialize the power state
2093 power_control::powerState = power_control::PowerState::off;
2094 // Check power good
2095 if (power_control::psPowerOKLine.get_value() > 0)
2096 {
2097 power_control::powerState = power_control::PowerState::on;
2098 }
2099
2100 // Initialize the power state storage
2101 if (power_control::initializePowerStateStorage() < 0)
2102 {
2103 return -1;
2104 }
2105
2106 // Check if we need to start the Power Restore policy
2107 power_control::powerRestorePolicyCheck();
2108
Vijay Khemka33a532d2019-11-14 16:50:35 -08002109 if (power_control::nmiOutLine)
2110 power_control::nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002111
2112 std::cerr << "Initializing power state. ";
2113 power_control::logStateTransition(power_control::powerState);
2114
2115 // Power Control Service
2116 sdbusplus::asio::object_server hostServer =
2117 sdbusplus::asio::object_server(power_control::conn);
2118
2119 // Power Control Interface
2120 power_control::hostIface = hostServer.add_interface(
2121 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
2122
2123 power_control::hostIface->register_property(
2124 "RequestedHostTransition",
2125 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2126 [](const std::string& requested, std::string& resp) {
2127 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2128 {
2129 sendPowerControlEvent(
2130 power_control::Event::gracefulPowerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002131 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002132 }
2133 else if (requested ==
2134 "xyz.openbmc_project.State.Host.Transition.On")
2135 {
2136 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002137 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002138 }
2139 else if (requested ==
2140 "xyz.openbmc_project.State.Host.Transition.Reboot")
2141 {
Jason M. Billse7520ba2020-01-31 11:19:03 -08002142 sendPowerControlEvent(power_control::Event::powerCycleRequest);
2143 addRestartCause(power_control::RestartCause::command);
2144 }
2145 else if (requested == "xyz.openbmc_project.State.Host.Transition."
2146 "GracefulWarmReboot")
2147 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002148 sendPowerControlEvent(
2149 power_control::Event::gracefulPowerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002150 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002151 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08002152 else if (requested == "xyz.openbmc_project.State.Host.Transition."
2153 "ForceWarmReboot")
2154 {
2155 sendPowerControlEvent(power_control::Event::resetRequest);
2156 addRestartCause(power_control::RestartCause::command);
2157 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002158 else
2159 {
2160 std::cerr << "Unrecognized host state transition request.\n";
2161 throw std::invalid_argument("Unrecognized Transition Request");
2162 return 0;
2163 }
2164 resp = requested;
2165 return 1;
2166 });
2167 power_control::hostIface->register_property(
2168 "CurrentHostState",
2169 std::string(power_control::getHostState(power_control::powerState)));
2170
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002171 power_control::hostIface->initialize();
2172
2173 // Chassis Control Service
2174 sdbusplus::asio::object_server chassisServer =
2175 sdbusplus::asio::object_server(power_control::conn);
2176
2177 // Chassis Control Interface
2178 power_control::chassisIface =
2179 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
2180 "xyz.openbmc_project.State.Chassis");
2181
2182 power_control::chassisIface->register_property(
2183 "RequestedPowerTransition",
2184 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2185 [](const std::string& requested, std::string& resp) {
2186 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2187 {
2188 sendPowerControlEvent(power_control::Event::powerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002189 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002190 }
2191 else if (requested ==
2192 "xyz.openbmc_project.State.Chassis.Transition.On")
2193 {
2194 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002195 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002196 }
2197 else if (requested ==
2198 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2199 {
2200 sendPowerControlEvent(power_control::Event::powerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002201 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002202 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002203 else
2204 {
2205 std::cerr << "Unrecognized chassis state transition request.\n";
2206 throw std::invalid_argument("Unrecognized Transition Request");
2207 return 0;
2208 }
2209 resp = requested;
2210 return 1;
2211 });
2212 power_control::chassisIface->register_property(
2213 "CurrentPowerState",
2214 std::string(power_control::getChassisState(power_control::powerState)));
2215 power_control::chassisIface->register_property(
2216 "LastStateChangeTime", power_control::getCurrentTimeMs());
2217
2218 power_control::chassisIface->initialize();
2219
2220 // Buttons Service
2221 sdbusplus::asio::object_server buttonsServer =
2222 sdbusplus::asio::object_server(power_control::conn);
2223
2224 // Power Button Interface
2225 power_control::powerButtonIface = buttonsServer.add_interface(
2226 "/xyz/openbmc_project/chassis/buttons/power",
2227 "xyz.openbmc_project.Chassis.Buttons");
2228
2229 power_control::powerButtonIface->register_property(
2230 "ButtonMasked", false, [](const bool requested, bool& current) {
2231 if (requested)
2232 {
2233 if (power_control::powerButtonMask)
2234 {
2235 return 1;
2236 }
2237 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002238 power_control::powerOutName, 1,
2239 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002240 {
2241 throw std::runtime_error("Failed to request GPIO");
2242 return 0;
2243 }
2244 std::cerr << "Power Button Masked.\n";
2245 }
2246 else
2247 {
2248 if (!power_control::powerButtonMask)
2249 {
2250 return 1;
2251 }
2252 std::cerr << "Power Button Un-masked\n";
2253 power_control::powerButtonMask.reset();
2254 }
2255 // Update the mask setting
2256 current = requested;
2257 return 1;
2258 });
2259
2260 // Check power button state
2261 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2262 power_control::powerButtonIface->register_property("ButtonPressed",
2263 powerButtonPressed);
2264
2265 power_control::powerButtonIface->initialize();
2266
2267 // Reset Button Interface
2268 power_control::resetButtonIface = buttonsServer.add_interface(
2269 "/xyz/openbmc_project/chassis/buttons/reset",
2270 "xyz.openbmc_project.Chassis.Buttons");
2271
2272 power_control::resetButtonIface->register_property(
2273 "ButtonMasked", false, [](const bool requested, bool& current) {
2274 if (requested)
2275 {
2276 if (power_control::resetButtonMask)
2277 {
2278 return 1;
2279 }
2280 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002281 power_control::resetOutName, 1,
2282 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002283 {
2284 throw std::runtime_error("Failed to request GPIO");
2285 return 0;
2286 }
2287 std::cerr << "Reset Button Masked.\n";
2288 }
2289 else
2290 {
2291 if (!power_control::resetButtonMask)
2292 {
2293 return 1;
2294 }
2295 std::cerr << "Reset Button Un-masked\n";
2296 power_control::resetButtonMask.reset();
2297 }
2298 // Update the mask setting
2299 current = requested;
2300 return 1;
2301 });
2302
2303 // Check reset button state
2304 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2305 power_control::resetButtonIface->register_property("ButtonPressed",
2306 resetButtonPressed);
2307
2308 power_control::resetButtonIface->initialize();
2309
Vijay Khemka33a532d2019-11-14 16:50:35 -08002310 if (power_control::nmiButtonLine)
2311 {
2312 // NMI Button Interface
2313 power_control::nmiButtonIface = buttonsServer.add_interface(
2314 "/xyz/openbmc_project/chassis/buttons/nmi",
2315 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002316
Vijay Khemka33a532d2019-11-14 16:50:35 -08002317 power_control::nmiButtonIface->register_property(
2318 "ButtonMasked", false, [](const bool requested, bool& current) {
2319 if (power_control::nmiButtonMasked == requested)
2320 {
2321 // NMI button mask is already set as requested, so no change
2322 return 1;
2323 }
2324 if (requested)
2325 {
2326 std::cerr << "NMI Button Masked.\n";
2327 power_control::nmiButtonMasked = true;
2328 }
2329 else
2330 {
2331 std::cerr << "NMI Button Un-masked.\n";
2332 power_control::nmiButtonMasked = false;
2333 }
2334 // Update the mask setting
2335 current = power_control::nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002336 return 1;
Vijay Khemka33a532d2019-11-14 16:50:35 -08002337 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002338
Vijay Khemka33a532d2019-11-14 16:50:35 -08002339 // Check NMI button state
2340 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2341 power_control::nmiButtonIface->register_property("ButtonPressed",
2342 nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002343
Vijay Khemka33a532d2019-11-14 16:50:35 -08002344 power_control::nmiButtonIface->initialize();
2345 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002346
Vijay Khemka33a532d2019-11-14 16:50:35 -08002347 if (power_control::nmiOutLine)
2348 {
2349 // NMI out Service
2350 sdbusplus::asio::object_server nmiOutServer =
2351 sdbusplus::asio::object_server(power_control::conn);
Chen Yugang174ec662019-08-19 19:58:49 +08002352
Vijay Khemka33a532d2019-11-14 16:50:35 -08002353 // NMI out Interface
2354 power_control::nmiOutIface =
2355 nmiOutServer.add_interface("/xyz/openbmc_project/control/host0/nmi",
2356 "xyz.openbmc_project.Control.Host.NMI");
2357 power_control::nmiOutIface->register_method("NMI",
2358 power_control::nmiReset);
2359 power_control::nmiOutIface->initialize();
2360 }
Chen Yugang174ec662019-08-19 19:58:49 +08002361
Vijay Khemka33a532d2019-11-14 16:50:35 -08002362 if (power_control::idButtonLine)
2363 {
2364 // ID Button Interface
2365 power_control::idButtonIface = buttonsServer.add_interface(
2366 "/xyz/openbmc_project/chassis/buttons/id",
2367 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002368
Vijay Khemka33a532d2019-11-14 16:50:35 -08002369 // Check ID button state
2370 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2371 power_control::idButtonIface->register_property("ButtonPressed",
2372 idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002373
Vijay Khemka33a532d2019-11-14 16:50:35 -08002374 power_control::idButtonIface->initialize();
2375 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002376
2377 // OS State Service
2378 sdbusplus::asio::object_server osServer =
2379 sdbusplus::asio::object_server(power_control::conn);
2380
2381 // OS State Interface
2382 power_control::osIface = osServer.add_interface(
2383 "/xyz/openbmc_project/state/os",
2384 "xyz.openbmc_project.State.OperatingSystem.Status");
2385
2386 // Get the initial OS state based on POST complete
2387 // 0: Asserted, OS state is "Standby" (ready to boot)
2388 // 1: De-Asserted, OS state is "Inactive"
2389 std::string osState = power_control::postCompleteLine.get_value() > 0
2390 ? "Inactive"
2391 : "Standby";
2392
2393 power_control::osIface->register_property("OperatingSystemState",
2394 std::string(osState));
2395
2396 power_control::osIface->initialize();
2397
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002398 // Restart Cause Service
2399 sdbusplus::asio::object_server restartCauseServer =
2400 sdbusplus::asio::object_server(power_control::conn);
2401
2402 // Restart Cause Interface
2403 power_control::restartCauseIface = restartCauseServer.add_interface(
2404 "/xyz/openbmc_project/control/host0/restart_cause",
2405 "xyz.openbmc_project.Control.Host.RestartCause");
2406
2407 power_control::restartCauseIface->register_property(
2408 "RestartCause",
2409 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
2410
2411 power_control::restartCauseIface->register_property(
2412 "RequestedRestartCause",
2413 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
2414 [](const std::string& requested, std::string& resp) {
2415 if (requested ==
2416 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
2417 {
2418 power_control::addRestartCause(
2419 power_control::RestartCause::watchdog);
2420 }
2421 else
2422 {
2423 throw std::invalid_argument(
2424 "Unrecognized RestartCause Request");
2425 return 0;
2426 }
2427
2428 std::cerr << "RestartCause requested: " << requested << "\n";
2429 resp = requested;
2430 return 1;
2431 });
2432
2433 power_control::restartCauseIface->initialize();
2434
Yong Li8d660212019-12-27 10:18:10 +08002435 power_control::currentHostStateMonitor();
2436
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002437 power_control::io.run();
2438
2439 return 0;
2440}