blob: d3a2054e62e1b32c89e02d4c1dfb1fc5cf953c24 [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>
Vijay Khemkafc1ecc52020-04-01 10:49:28 -070028#include <phosphor-logging/log.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070029#include <sdbusplus/asio/object_server.hpp>
30#include <string_view>
31
32namespace power_control
33{
34static boost::asio::io_service io;
35std::shared_ptr<sdbusplus::asio::connection> conn;
36static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
37static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
38static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
39static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
40static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
41static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
42static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
Chen Yugang174ec662019-08-19 19:58:49 +080043static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070044static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070045
46static gpiod::line powerButtonMask;
47static gpiod::line resetButtonMask;
48static bool nmiButtonMasked = false;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070049
50const static constexpr int powerPulseTimeMs = 200;
51const static constexpr int forceOffPulseTimeMs = 15000;
52const static constexpr int resetPulseTimeMs = 500;
Jason M. Billsfc9408a2020-01-31 14:54:17 -080053const static constexpr int powerCycleTimeMs = 5000;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070054const static constexpr int sioPowerGoodWatchdogTimeMs = 1000;
55const static constexpr int psPowerOKWatchdogTimeMs = 8000;
56const static constexpr int gracefulPowerOffTimeMs = 60000;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -070057const static constexpr int warmResetCheckTimeMs = 500;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070058const static constexpr int buttonMaskTimeMs = 60000;
59const static constexpr int powerOffSaveTimeMs = 7000;
60
61const static std::filesystem::path powerControlDir = "/var/lib/power-control";
62const static constexpr std::string_view powerStateFile = "power-state";
63
64static bool nmiEnabled = true;
65static constexpr const char* nmiOutName = "NMI_OUT";
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -070066static constexpr const char* powerOutName = "POWER_OUT";
67static constexpr const char* resetOutName = "RESET_OUT";
Ed Tanousf61ca6f2019-08-15 15:09:05 -070068
69// Timers
70// Time holding GPIOs asserted
71static boost::asio::steady_timer gpioAssertTimer(io);
72// Time between off and on during a power cycle
73static boost::asio::steady_timer powerCycleTimer(io);
74// Time OS gracefully powering off
75static boost::asio::steady_timer gracefulPowerOffTimer(io);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -070076// Time the warm reset check
77static boost::asio::steady_timer warmResetCheckTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -070078// Time power supply power OK assertion on power-on
79static boost::asio::steady_timer psPowerOKWatchdogTimer(io);
80// Time SIO power good assertion on power-on
81static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
82// Time power-off state save for power loss tracking
83static boost::asio::steady_timer powerStateSaveTimer(io);
84// POH timer
85static boost::asio::steady_timer pohCounterTimer(io);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070086// Time when to allow restart cause updates
87static boost::asio::steady_timer restartCauseTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -070088
89// GPIO Lines and Event Descriptors
90static gpiod::line psPowerOKLine;
91static boost::asio::posix::stream_descriptor psPowerOKEvent(io);
92static gpiod::line sioPowerGoodLine;
93static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
94static gpiod::line sioOnControlLine;
95static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
96static gpiod::line sioS5Line;
97static boost::asio::posix::stream_descriptor sioS5Event(io);
98static gpiod::line powerButtonLine;
99static boost::asio::posix::stream_descriptor powerButtonEvent(io);
100static gpiod::line resetButtonLine;
101static boost::asio::posix::stream_descriptor resetButtonEvent(io);
102static gpiod::line nmiButtonLine;
103static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
104static gpiod::line idButtonLine;
105static boost::asio::posix::stream_descriptor idButtonEvent(io);
106static gpiod::line postCompleteLine;
107static boost::asio::posix::stream_descriptor postCompleteEvent(io);
108static gpiod::line nmiOutLine;
109
110static constexpr uint8_t beepPowerFail = 8;
111
112static void beep(const uint8_t& beepPriority)
113{
114 std::cerr << "Beep with priority: " << (unsigned)beepPriority << "\n";
115
116 conn->async_method_call(
117 [](boost::system::error_code ec) {
118 if (ec)
119 {
120 std::cerr << "beep returned error with "
121 "async_method_call (ec = "
122 << ec << ")\n";
123 return;
124 }
125 },
126 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
127 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
128}
129
130enum class PowerState
131{
132 on,
133 waitForPSPowerOK,
134 waitForSIOPowerGood,
135 failedTransitionToOn,
136 off,
137 transitionToOff,
138 gracefulTransitionToOff,
139 cycleOff,
140 transitionToCycleOff,
141 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700142 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700143};
144static PowerState powerState;
145static std::string getPowerStateName(PowerState state)
146{
147 switch (state)
148 {
149 case PowerState::on:
150 return "On";
151 break;
152 case PowerState::waitForPSPowerOK:
153 return "Wait for Power Supply Power OK";
154 break;
155 case PowerState::waitForSIOPowerGood:
156 return "Wait for SIO Power Good";
157 break;
158 case PowerState::failedTransitionToOn:
159 return "Failed Transition to On";
160 break;
161 case PowerState::off:
162 return "Off";
163 break;
164 case PowerState::transitionToOff:
165 return "Transition to Off";
166 break;
167 case PowerState::gracefulTransitionToOff:
168 return "Graceful Transition to Off";
169 break;
170 case PowerState::cycleOff:
171 return "Power Cycle Off";
172 break;
173 case PowerState::transitionToCycleOff:
174 return "Transition to Power Cycle Off";
175 break;
176 case PowerState::gracefulTransitionToCycleOff:
177 return "Graceful Transition to Power Cycle Off";
178 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700179 case PowerState::checkForWarmReset:
180 return "Check for Warm Reset";
181 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700182 default:
183 return "unknown state: " + std::to_string(static_cast<int>(state));
184 break;
185 }
186}
187static void logStateTransition(const PowerState state)
188{
Vijay Khemkafc1ecc52020-04-01 10:49:28 -0700189 std::string logMsg = "Moving to \"" + getPowerStateName(state) + "\" state";
190 phosphor::logging::log<phosphor::logging::level::INFO>(
191 logMsg.c_str(),
192 phosphor::logging::entry("STATE=%s", getPowerStateName(state).c_str()));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700193}
194
195enum class Event
196{
197 psPowerOKAssert,
198 psPowerOKDeAssert,
199 sioPowerGoodAssert,
200 sioPowerGoodDeAssert,
201 sioS5Assert,
202 sioS5DeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700203 postCompleteAssert,
204 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700205 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700206 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700207 powerCycleTimerExpired,
208 psPowerOKWatchdogTimerExpired,
209 sioPowerGoodWatchdogTimerExpired,
210 gracefulPowerOffTimerExpired,
211 powerOnRequest,
212 powerOffRequest,
213 powerCycleRequest,
214 resetRequest,
215 gracefulPowerOffRequest,
216 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700217 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700218};
219static std::string getEventName(Event event)
220{
221 switch (event)
222 {
223 case Event::psPowerOKAssert:
224 return "power supply power OK assert";
225 break;
226 case Event::psPowerOKDeAssert:
227 return "power supply power OK de-assert";
228 break;
229 case Event::sioPowerGoodAssert:
230 return "SIO power good assert";
231 break;
232 case Event::sioPowerGoodDeAssert:
233 return "SIO power good de-assert";
234 break;
235 case Event::sioS5Assert:
236 return "SIO S5 assert";
237 break;
238 case Event::sioS5DeAssert:
239 return "SIO S5 de-assert";
240 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700241 case Event::postCompleteAssert:
242 return "POST Complete assert";
243 break;
244 case Event::postCompleteDeAssert:
245 return "POST Complete de-assert";
246 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700247 case Event::powerButtonPressed:
248 return "power button pressed";
249 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700250 case Event::resetButtonPressed:
251 return "reset button pressed";
252 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700253 case Event::powerCycleTimerExpired:
254 return "power cycle timer expired";
255 break;
256 case Event::psPowerOKWatchdogTimerExpired:
257 return "power supply power OK watchdog timer expired";
258 break;
259 case Event::sioPowerGoodWatchdogTimerExpired:
260 return "SIO power good watchdog timer expired";
261 break;
262 case Event::gracefulPowerOffTimerExpired:
263 return "graceful power-off timer expired";
264 break;
265 case Event::powerOnRequest:
266 return "power-on request";
267 break;
268 case Event::powerOffRequest:
269 return "power-off request";
270 break;
271 case Event::powerCycleRequest:
272 return "power-cycle request";
273 break;
274 case Event::resetRequest:
275 return "reset request";
276 break;
277 case Event::gracefulPowerOffRequest:
278 return "graceful power-off request";
279 break;
280 case Event::gracefulPowerCycleRequest:
281 return "graceful power-cycle request";
282 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700283 case Event::warmResetDetected:
284 return "warm reset detected";
285 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700286 default:
287 return "unknown event: " + std::to_string(static_cast<int>(event));
288 break;
289 }
290}
291static void logEvent(const std::string_view stateHandler, const Event event)
292{
Vijay Khemkafc1ecc52020-04-01 10:49:28 -0700293 std::string logMsg{stateHandler};
294 logMsg += ": " + getEventName(event) + " event received";
295 phosphor::logging::log<phosphor::logging::level::INFO>(
296 logMsg.c_str(),
297 phosphor::logging::entry("EVENT=%s", getEventName(event).c_str()));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700298}
299
300// Power state handlers
301static void powerStateOn(const Event event);
302static void powerStateWaitForPSPowerOK(const Event event);
303static void powerStateWaitForSIOPowerGood(const Event event);
304static void powerStateFailedTransitionToOn(const Event event);
305static void powerStateOff(const Event event);
306static void powerStateTransitionToOff(const Event event);
307static void powerStateGracefulTransitionToOff(const Event event);
308static void powerStateCycleOff(const Event event);
309static void powerStateTransitionToCycleOff(const Event event);
310static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700311static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700312
313static std::function<void(const Event)> getPowerStateHandler(PowerState state)
314{
315 switch (state)
316 {
317 case PowerState::on:
318 return powerStateOn;
319 break;
320 case PowerState::waitForPSPowerOK:
321 return powerStateWaitForPSPowerOK;
322 break;
323 case PowerState::waitForSIOPowerGood:
324 return powerStateWaitForSIOPowerGood;
325 break;
326 case PowerState::failedTransitionToOn:
327 return powerStateFailedTransitionToOn;
328 break;
329 case PowerState::off:
330 return powerStateOff;
331 break;
332 case PowerState::transitionToOff:
333 return powerStateTransitionToOff;
334 break;
335 case PowerState::gracefulTransitionToOff:
336 return powerStateGracefulTransitionToOff;
337 break;
338 case PowerState::cycleOff:
339 return powerStateCycleOff;
340 break;
341 case PowerState::transitionToCycleOff:
342 return powerStateTransitionToCycleOff;
343 break;
344 case PowerState::gracefulTransitionToCycleOff:
345 return powerStateGracefulTransitionToCycleOff;
346 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700347 case PowerState::checkForWarmReset:
348 return powerStateCheckForWarmReset;
349 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700350 default:
351 return std::function<void(const Event)>{};
352 break;
353 }
354};
355
356static void sendPowerControlEvent(const Event event)
357{
358 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
359 if (handler == nullptr)
360 {
361 std::cerr << "Failed to find handler for power state: "
362 << static_cast<int>(powerState) << "\n";
363 return;
364 }
365 handler(event);
366}
367
368static uint64_t getCurrentTimeMs()
369{
370 struct timespec time = {};
371
372 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
373 {
374 return 0;
375 }
376 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
377 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
378
379 return currentTimeMs;
380}
381
382static constexpr std::string_view getHostState(const PowerState state)
383{
384 switch (state)
385 {
386 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700387 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700388 case PowerState::gracefulTransitionToCycleOff:
389 return "xyz.openbmc_project.State.Host.HostState.Running";
390 break;
391 case PowerState::waitForPSPowerOK:
392 case PowerState::waitForSIOPowerGood:
393 case PowerState::failedTransitionToOn:
394 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700395 case PowerState::transitionToOff:
396 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700397 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700398 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700399 return "xyz.openbmc_project.State.Host.HostState.Off";
400 break;
401 default:
402 return "";
403 break;
404 }
405};
406static constexpr std::string_view getChassisState(const PowerState state)
407{
408 switch (state)
409 {
410 case PowerState::on:
411 case PowerState::transitionToOff:
412 case PowerState::gracefulTransitionToOff:
413 case PowerState::transitionToCycleOff:
414 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700415 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700416 return "xyz.openbmc_project.State.Chassis.PowerState.On";
417 break;
418 case PowerState::waitForPSPowerOK:
419 case PowerState::waitForSIOPowerGood:
420 case PowerState::failedTransitionToOn:
421 case PowerState::off:
422 case PowerState::cycleOff:
423 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
424 break;
425 default:
426 return "";
427 break;
428 }
429};
430static void savePowerState(const PowerState state)
431{
432 powerStateSaveTimer.expires_after(
433 std::chrono::milliseconds(powerOffSaveTimeMs));
434 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
435 if (ec)
436 {
437 // operation_aborted is expected if timer is canceled before
438 // completion.
439 if (ec != boost::asio::error::operation_aborted)
440 {
441 std::cerr << "Power-state save async_wait failed: "
442 << ec.message() << "\n";
443 }
444 return;
445 }
446 std::ofstream powerStateStream(powerControlDir / powerStateFile);
447 powerStateStream << getChassisState(state);
448 });
449}
450static void setPowerState(const PowerState state)
451{
452 powerState = state;
453 logStateTransition(state);
454
455 hostIface->set_property("CurrentHostState",
456 std::string(getHostState(powerState)));
457
458 chassisIface->set_property("CurrentPowerState",
459 std::string(getChassisState(powerState)));
460 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
461
462 // Save the power state for the restore policy
463 savePowerState(state);
464}
465
466enum class RestartCause
467{
468 command,
469 resetButton,
470 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700471 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700472 powerPolicyOn,
473 powerPolicyRestore,
474 softReset,
475};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700476static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700477static std::string getRestartCause(RestartCause cause)
478{
479 switch (cause)
480 {
481 case RestartCause::command:
482 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
483 break;
484 case RestartCause::resetButton:
485 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
486 break;
487 case RestartCause::powerButton:
488 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
489 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700490 case RestartCause::watchdog:
491 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
492 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700493 case RestartCause::powerPolicyOn:
494 return "xyz.openbmc_project.State.Host.RestartCause."
495 "PowerPolicyAlwaysOn";
496 break;
497 case RestartCause::powerPolicyRestore:
498 return "xyz.openbmc_project.State.Host.RestartCause."
499 "PowerPolicyPreviousState";
500 break;
501 case RestartCause::softReset:
502 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
503 break;
504 default:
505 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
506 break;
507 }
508}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700509static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700510{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700511 // Add this to the set of causes for this restart
512 causeSet.insert(cause);
513}
514static void clearRestartCause()
515{
516 // Clear the set for the next restart
517 causeSet.clear();
518}
519static void setRestartCauseProperty(const std::string& cause)
520{
521 std::cerr << "RestartCause set to " << cause << "\n";
522 restartCauseIface->set_property("RestartCause", cause);
523}
Rashmi RV89f61312020-01-22 15:41:50 +0530524
525static void resetACBootProperty()
526{
527 if ((causeSet.contains(RestartCause::command)) ||
528 (causeSet.contains(RestartCause::softReset)))
529 {
530 conn->async_method_call(
531 [](boost::system::error_code ec) {
532 if (ec)
533 {
534 std::cerr << "failed to reset ACBoot property\n";
535 }
536 },
537 "xyz.openbmc_project.Settings",
538 "/xyz/openbmc_project/control/host0/ac_boot",
539 "org.freedesktop.DBus.Properties", "Set",
540 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
541 std::variant<std::string>{"False"});
542 }
543}
544
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700545static void setRestartCause()
546{
547 // Determine the actual restart cause based on the set of causes
548 std::string restartCause =
549 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
550 if (causeSet.contains(RestartCause::watchdog))
551 {
552 restartCause = getRestartCause(RestartCause::watchdog);
553 }
554 else if (causeSet.contains(RestartCause::command))
555 {
556 restartCause = getRestartCause(RestartCause::command);
557 }
558 else if (causeSet.contains(RestartCause::resetButton))
559 {
560 restartCause = getRestartCause(RestartCause::resetButton);
561 }
562 else if (causeSet.contains(RestartCause::powerButton))
563 {
564 restartCause = getRestartCause(RestartCause::powerButton);
565 }
566 else if (causeSet.contains(RestartCause::powerPolicyOn))
567 {
568 restartCause = getRestartCause(RestartCause::powerPolicyOn);
569 }
570 else if (causeSet.contains(RestartCause::powerPolicyRestore))
571 {
572 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
573 }
574 else if (causeSet.contains(RestartCause::softReset))
575 {
576 restartCause = getRestartCause(RestartCause::softReset);
577 }
578
579 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700580}
581
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700582static void systemPowerGoodFailedLog()
583{
584 sd_journal_send(
585 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
586 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
587 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
588 sioPowerGoodWatchdogTimeMs, NULL);
589}
590
591static void psPowerOKFailedLog()
592{
593 sd_journal_send(
594 "MESSAGE=PowerControl: power supply power good failed to assert",
595 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
596 "OpenBMC.0.1.PowerSupplyPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
597 psPowerOKWatchdogTimeMs, NULL);
598}
599
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700600static void powerRestorePolicyLog()
601{
602 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
603 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
604 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
605}
606
607static void powerButtonPressLog()
608{
609 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
610 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
611 "OpenBMC.0.1.PowerButtonPressed", NULL);
612}
613
614static void resetButtonPressLog()
615{
616 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
617 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
618 "OpenBMC.0.1.ResetButtonPressed", NULL);
619}
620
621static void nmiButtonPressLog()
622{
623 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
624 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
625 "OpenBMC.0.1.NMIButtonPressed", NULL);
626}
627
628static void nmiDiagIntLog()
629{
630 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
631 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
632 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
633}
634
635static int initializePowerStateStorage()
636{
637 // create the power control directory if it doesn't exist
638 std::error_code ec;
639 if (!(std::filesystem::create_directories(powerControlDir, ec)))
640 {
641 if (ec.value() != 0)
642 {
643 std::cerr << "failed to create " << powerControlDir << ": "
644 << ec.message() << "\n";
645 return -1;
646 }
647 }
648 // Create the power state file if it doesn't exist
649 if (!std::filesystem::exists(powerControlDir / powerStateFile))
650 {
651 std::ofstream powerStateStream(powerControlDir / powerStateFile);
652 powerStateStream << getChassisState(powerState);
653 }
654 return 0;
655}
656
657static bool wasPowerDropped()
658{
659 std::ifstream powerStateStream(powerControlDir / powerStateFile);
660 if (!powerStateStream.is_open())
661 {
662 std::cerr << "Failed to open power state file\n";
663 return false;
664 }
665
666 std::string state;
667 std::getline(powerStateStream, state);
668 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
669}
670
671static void invokePowerRestorePolicy(const std::string& policy)
672{
673 // Async events may call this twice, but we only want to run once
674 static bool policyInvoked = false;
675 if (policyInvoked)
676 {
677 return;
678 }
679 policyInvoked = true;
680
681 std::cerr << "Power restore delay expired, invoking " << policy << "\n";
682 if (policy ==
683 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
684 {
685 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700686 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700687 }
688 else if (policy == "xyz.openbmc_project.Control.Power.RestorePolicy."
689 "Policy.Restore")
690 {
691 if (wasPowerDropped())
692 {
693 std::cerr << "Power was dropped, restoring Host On state\n";
694 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700695 setRestartCauseProperty(
696 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700697 }
698 else
699 {
700 std::cerr << "No power drop, restoring Host Off state\n";
701 }
702 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -0700703 // We're done with the previous power state for the restore policy, so store
704 // the current state
705 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700706}
707
708static void powerRestorePolicyDelay(int delay)
709{
710 // Async events may call this twice, but we only want to run once
711 static bool delayStarted = false;
712 if (delayStarted)
713 {
714 return;
715 }
716 delayStarted = true;
717 // Calculate the delay from now to meet the requested delay
718 // Subtract the approximate uboot time
719 static constexpr const int ubootSeconds = 20;
720 delay -= ubootSeconds;
721 // Subtract the time since boot
722 struct sysinfo info = {};
723 if (sysinfo(&info) == 0)
724 {
725 delay -= info.uptime;
726 }
727 // 0 is the minimum delay
728 delay = std::max(delay, 0);
729
730 static boost::asio::steady_timer powerRestorePolicyTimer(io);
731 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
732 std::cerr << "Power restore delay of " << delay << " seconds started\n";
733 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
734 if (ec)
735 {
736 // operation_aborted is expected if timer is canceled before
737 // completion.
738 if (ec != boost::asio::error::operation_aborted)
739 {
740 std::cerr << "power restore policy async_wait failed: "
741 << ec.message() << "\n";
742 }
743 return;
744 }
745 // Get Power Restore Policy
746 // In case PowerRestorePolicy is not available, set a match for it
747 static std::unique_ptr<sdbusplus::bus::match::match>
748 powerRestorePolicyMatch = std::make_unique<
749 sdbusplus::bus::match::match>(
750 *conn,
751 "type='signal',interface='org.freedesktop.DBus.Properties',"
752 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
753 "project.Control.Power.RestorePolicy'",
754 [](sdbusplus::message::message& msg) {
755 std::string interfaceName;
756 boost::container::flat_map<std::string,
757 std::variant<std::string>>
758 propertiesChanged;
759 std::string policy;
760 try
761 {
762 msg.read(interfaceName, propertiesChanged);
763 policy = std::get<std::string>(
764 propertiesChanged.begin()->second);
765 }
766 catch (std::exception& e)
767 {
768 std::cerr
769 << "Unable to read power restore policy value\n";
770 powerRestorePolicyMatch.reset();
771 return;
772 }
773 invokePowerRestorePolicy(policy);
774 powerRestorePolicyMatch.reset();
775 });
776
777 // Check if it's already on DBus
778 conn->async_method_call(
779 [](boost::system::error_code ec,
780 const std::variant<std::string>& policyProperty) {
781 if (ec)
782 {
783 return;
784 }
785 powerRestorePolicyMatch.reset();
786 const std::string* policy =
787 std::get_if<std::string>(&policyProperty);
788 if (policy == nullptr)
789 {
790 std::cerr << "Unable to read power restore policy value\n";
791 return;
792 }
793 invokePowerRestorePolicy(*policy);
794 },
795 "xyz.openbmc_project.Settings",
796 "/xyz/openbmc_project/control/host0/power_restore_policy",
797 "org.freedesktop.DBus.Properties", "Get",
798 "xyz.openbmc_project.Control.Power.RestorePolicy",
799 "PowerRestorePolicy");
800 });
801}
802
803static void powerRestorePolicyStart()
804{
805 std::cerr << "Power restore policy started\n";
806 powerRestorePolicyLog();
807
808 // Get the desired delay time
809 // In case PowerRestoreDelay is not available, set a match for it
810 static std::unique_ptr<sdbusplus::bus::match::match>
811 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
812 *conn,
813 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
814 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
815 "Power.RestoreDelay'",
816 [](sdbusplus::message::message& msg) {
817 std::string interfaceName;
818 boost::container::flat_map<std::string, std::variant<uint16_t>>
819 propertiesChanged;
820 int delay = 0;
821 try
822 {
823 msg.read(interfaceName, propertiesChanged);
824 delay =
825 std::get<uint16_t>(propertiesChanged.begin()->second);
826 }
827 catch (std::exception& e)
828 {
829 std::cerr << "Unable to read power restore delay value\n";
830 powerRestoreDelayMatch.reset();
831 return;
832 }
833 powerRestorePolicyDelay(delay);
834 powerRestoreDelayMatch.reset();
835 });
836
837 // Check if it's already on DBus
838 conn->async_method_call(
839 [](boost::system::error_code ec,
840 const std::variant<uint16_t>& delayProperty) {
841 if (ec)
842 {
843 return;
844 }
845 powerRestoreDelayMatch.reset();
846 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
847 if (delay == nullptr)
848 {
849 std::cerr << "Unable to read power restore delay value\n";
850 return;
851 }
852 powerRestorePolicyDelay(*delay);
853 },
854 "xyz.openbmc_project.Settings",
855 "/xyz/openbmc_project/control/power_restore_delay",
856 "org.freedesktop.DBus.Properties", "Get",
857 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
858}
859
860static void powerRestorePolicyCheck()
861{
862 // In case ACBoot is not available, set a match for it
863 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
864 std::make_unique<sdbusplus::bus::match::match>(
865 *conn,
866 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
867 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
868 "ACBoot'",
869 [](sdbusplus::message::message& msg) {
870 std::string interfaceName;
871 boost::container::flat_map<std::string,
872 std::variant<std::string>>
873 propertiesChanged;
874 std::string acBoot;
875 try
876 {
877 msg.read(interfaceName, propertiesChanged);
878 acBoot = std::get<std::string>(
879 propertiesChanged.begin()->second);
880 }
881 catch (std::exception& e)
882 {
883 std::cerr << "Unable to read AC Boot status\n";
884 acBootMatch.reset();
885 return;
886 }
887 if (acBoot == "Unknown")
888 {
889 return;
890 }
891 if (acBoot == "True")
892 {
893 // Start the Power Restore policy
894 powerRestorePolicyStart();
895 }
896 acBootMatch.reset();
897 });
898
899 // Check if it's already on DBus
900 conn->async_method_call(
901 [](boost::system::error_code ec,
902 const std::variant<std::string>& acBootProperty) {
903 if (ec)
904 {
905 return;
906 }
907 const std::string* acBoot =
908 std::get_if<std::string>(&acBootProperty);
909 if (acBoot == nullptr)
910 {
911 std::cerr << "Unable to read AC Boot status\n";
912 return;
913 }
914 if (*acBoot == "Unknown")
915 {
916 return;
917 }
918 if (*acBoot == "True")
919 {
920 // Start the Power Restore policy
921 powerRestorePolicyStart();
922 }
923 acBootMatch.reset();
924 },
925 "xyz.openbmc_project.Settings",
926 "/xyz/openbmc_project/control/host0/ac_boot",
927 "org.freedesktop.DBus.Properties", "Get",
928 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
929}
930
931static bool requestGPIOEvents(
932 const std::string& name, const std::function<void()>& handler,
933 gpiod::line& gpioLine,
934 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
935{
936 // Find the GPIO line
937 gpioLine = gpiod::find_line(name);
938 if (!gpioLine)
939 {
940 std::cerr << "Failed to find the " << name << " line\n";
941 return false;
942 }
943
944 try
945 {
946 gpioLine.request(
947 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
948 }
949 catch (std::exception&)
950 {
951 std::cerr << "Failed to request events for " << name << "\n";
952 return false;
953 }
954
955 int gpioLineFd = gpioLine.event_get_fd();
956 if (gpioLineFd < 0)
957 {
958 std::cerr << "Failed to get " << name << " fd\n";
959 return false;
960 }
961
962 gpioEventDescriptor.assign(gpioLineFd);
963
964 gpioEventDescriptor.async_wait(
965 boost::asio::posix::stream_descriptor::wait_read,
966 [&name, handler](const boost::system::error_code ec) {
967 if (ec)
968 {
969 std::cerr << name << " fd handler error: " << ec.message()
970 << "\n";
971 // TODO: throw here to force power-control to restart?
972 return;
973 }
974 handler();
975 });
976 return true;
977}
978
979static bool setGPIOOutput(const std::string& name, const int value,
980 gpiod::line& gpioLine)
981{
982 // Find the GPIO line
983 gpioLine = gpiod::find_line(name);
984 if (!gpioLine)
985 {
986 std::cerr << "Failed to find the " << name << " line.\n";
987 return false;
988 }
989
990 // Request GPIO output to specified value
991 try
992 {
993 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
994 value);
995 }
996 catch (std::exception&)
997 {
998 std::cerr << "Failed to request " << name << " output\n";
999 return false;
1000 }
1001
1002 std::cerr << name << " set to " << std::to_string(value) << "\n";
1003 return true;
1004}
1005
1006static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1007 const std::string& name, const int value,
1008 const int durationMs)
1009{
1010 // Set the masked GPIO line to the specified value
1011 maskedGPIOLine.set_value(value);
1012 std::cerr << name << " set to " << std::to_string(value) << "\n";
1013 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1014 gpioAssertTimer.async_wait(
1015 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
1016 // Set the masked GPIO line back to the opposite value
1017 maskedGPIOLine.set_value(!value);
1018 std::cerr << name << " released\n";
1019 if (ec)
1020 {
1021 // operation_aborted is expected if timer is canceled before
1022 // completion.
1023 if (ec != boost::asio::error::operation_aborted)
1024 {
1025 std::cerr << name << " async_wait failed: " + ec.message()
1026 << "\n";
1027 }
1028 }
1029 });
1030 return 0;
1031}
1032
1033static int setGPIOOutputForMs(const std::string& name, const int value,
1034 const int durationMs)
1035{
1036 // If the requested GPIO is masked, use the mask line to set the output
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001037 if (powerButtonMask && name == power_control::powerOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001038 {
1039 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
1040 durationMs);
1041 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001042 if (resetButtonMask && name == power_control::resetOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001043 {
1044 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
1045 durationMs);
1046 }
1047
1048 // No mask set, so request and set the GPIO normally
1049 gpiod::line gpioLine;
1050 if (!setGPIOOutput(name, value, gpioLine))
1051 {
1052 return -1;
1053 }
1054 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1055 gpioAssertTimer.async_wait(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001056 [gpioLine, value, name](const boost::system::error_code ec) {
1057 // Set the GPIO line back to the opposite value
1058 gpioLine.set_value(!value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001059 std::cerr << name << " released\n";
1060 if (ec)
1061 {
1062 // operation_aborted is expected if timer is canceled before
1063 // completion.
1064 if (ec != boost::asio::error::operation_aborted)
1065 {
1066 std::cerr << name << " async_wait failed: " << ec.message()
1067 << "\n";
1068 }
1069 }
1070 });
1071 return 0;
1072}
1073
1074static void powerOn()
1075{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001076 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001077}
1078
1079static void gracefulPowerOff()
1080{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001081 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001082}
1083
1084static void forcePowerOff()
1085{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001086 if (setGPIOOutputForMs(power_control::powerOutName, 0,
1087 forceOffPulseTimeMs) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001088 {
1089 return;
1090 }
1091
1092 // If the force off timer expires, then the PCH power-button override
1093 // failed, so attempt the Unconditional Powerdown SMBus command.
1094 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1095 if (ec)
1096 {
1097 // operation_aborted is expected if timer is canceled before
1098 // completion.
1099 if (ec != boost::asio::error::operation_aborted)
1100 {
1101 std::cerr << "Force power off async_wait failed: "
1102 << ec.message() << "\n";
1103 }
1104 return;
1105 }
1106 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
1107 "Powerdown SMBus command.\n";
1108 const static constexpr size_t pchDevBusAddress = 3;
1109 const static constexpr size_t pchDevSlaveAddress = 0x44;
1110 const static constexpr size_t pchCmdReg = 0;
1111 const static constexpr size_t pchPowerDownCmd = 0x02;
1112 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
1113 pchPowerDownCmd) < 0)
1114 {
1115 std::cerr << "Unconditional Powerdown command failed! Not sure "
1116 "what to do now.\n";
1117 }
1118 });
1119}
1120
1121static void reset()
1122{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001123 setGPIOOutputForMs(power_control::resetOutName, 0, resetPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001124}
1125
1126static void gracefulPowerOffTimerStart()
1127{
1128 std::cerr << "Graceful power-off timer started\n";
1129 gracefulPowerOffTimer.expires_after(
1130 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1131 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1132 if (ec)
1133 {
1134 // operation_aborted is expected if timer is canceled before
1135 // completion.
1136 if (ec != boost::asio::error::operation_aborted)
1137 {
1138 std::cerr << "Graceful power-off async_wait failed: "
1139 << ec.message() << "\n";
1140 }
1141 std::cerr << "Graceful power-off timer canceled\n";
1142 return;
1143 }
1144 std::cerr << "Graceful power-off timer completed\n";
1145 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1146 });
1147}
1148
1149static void powerCycleTimerStart()
1150{
1151 std::cerr << "Power-cycle timer started\n";
1152 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1153 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1154 if (ec)
1155 {
1156 // operation_aborted is expected if timer is canceled before
1157 // completion.
1158 if (ec != boost::asio::error::operation_aborted)
1159 {
1160 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1161 << "\n";
1162 }
1163 std::cerr << "Power-cycle timer canceled\n";
1164 return;
1165 }
1166 std::cerr << "Power-cycle timer completed\n";
1167 sendPowerControlEvent(Event::powerCycleTimerExpired);
1168 });
1169}
1170
1171static void psPowerOKWatchdogTimerStart()
1172{
1173 std::cerr << "power supply power OK watchdog timer started\n";
1174 psPowerOKWatchdogTimer.expires_after(
1175 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1176 psPowerOKWatchdogTimer.async_wait(
1177 [](const boost::system::error_code ec) {
1178 if (ec)
1179 {
1180 // operation_aborted is expected if timer is canceled before
1181 // completion.
1182 if (ec != boost::asio::error::operation_aborted)
1183 {
1184 std::cerr
1185 << "power supply power OK watchdog async_wait failed: "
1186 << ec.message() << "\n";
1187 }
1188 std::cerr << "power supply power OK watchdog timer canceled\n";
1189 return;
1190 }
1191 std::cerr << "power supply power OK watchdog timer expired\n";
1192 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1193 });
1194}
1195
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001196static void warmResetCheckTimerStart()
1197{
1198 std::cerr << "Warm reset check timer started\n";
1199 warmResetCheckTimer.expires_after(
1200 std::chrono::milliseconds(warmResetCheckTimeMs));
1201 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1202 if (ec)
1203 {
1204 // operation_aborted is expected if timer is canceled before
1205 // completion.
1206 if (ec != boost::asio::error::operation_aborted)
1207 {
1208 std::cerr << "Warm reset check async_wait failed: "
1209 << ec.message() << "\n";
1210 }
1211 std::cerr << "Warm reset check timer canceled\n";
1212 return;
1213 }
1214 std::cerr << "Warm reset check timer completed\n";
1215 sendPowerControlEvent(Event::warmResetDetected);
1216 });
1217}
1218
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001219static void pohCounterTimerStart()
1220{
1221 std::cerr << "POH timer started\n";
1222 // Set the time-out as 1 hour, to align with POH command in ipmid
1223 pohCounterTimer.expires_after(std::chrono::hours(1));
1224 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1225 if (ec)
1226 {
1227 // operation_aborted is expected if timer is canceled before
1228 // completion.
1229 if (ec != boost::asio::error::operation_aborted)
1230 {
1231 std::cerr << "POH timer async_wait failed: " << ec.message()
1232 << "\n";
1233 }
1234 std::cerr << "POH timer canceled\n";
1235 return;
1236 }
1237
1238 if (getHostState(powerState) !=
1239 "xyz.openbmc_project.State.Host.HostState.Running")
1240 {
1241 return;
1242 }
1243
1244 conn->async_method_call(
1245 [](boost::system::error_code ec,
1246 const std::variant<uint32_t>& pohCounterProperty) {
1247 if (ec)
1248 {
1249 std::cerr << "error to get poh counter\n";
1250 return;
1251 }
1252 const uint32_t* pohCounter =
1253 std::get_if<uint32_t>(&pohCounterProperty);
1254 if (pohCounter == nullptr)
1255 {
1256 std::cerr << "unable to read poh counter\n";
1257 return;
1258 }
1259
1260 conn->async_method_call(
1261 [](boost::system::error_code ec) {
1262 if (ec)
1263 {
1264 std::cerr << "failed to set poh counter\n";
1265 }
1266 },
1267 "xyz.openbmc_project.Settings",
1268 "/xyz/openbmc_project/state/chassis0",
1269 "org.freedesktop.DBus.Properties", "Set",
1270 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1271 std::variant<uint32_t>(*pohCounter + 1));
1272 },
1273 "xyz.openbmc_project.Settings",
1274 "/xyz/openbmc_project/state/chassis0",
1275 "org.freedesktop.DBus.Properties", "Get",
1276 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1277
1278 pohCounterTimerStart();
1279 });
1280}
1281
1282static void currentHostStateMonitor()
1283{
Yong Li8d660212019-12-27 10:18:10 +08001284 if (getHostState(powerState) ==
1285 "xyz.openbmc_project.State.Host.HostState.Running")
1286 {
1287 pohCounterTimerStart();
1288 // Clear the restart cause set for the next restart
1289 clearRestartCause();
1290 }
1291 else
1292 {
1293 pohCounterTimer.cancel();
1294 // Set the restart cause set for this restart
1295 setRestartCause();
1296 }
1297
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001298 static auto match = sdbusplus::bus::match::match(
1299 *conn,
1300 "type='signal',member='PropertiesChanged', "
1301 "interface='org.freedesktop.DBus.Properties', "
1302 "arg0namespace='xyz.openbmc_project.State.Host'",
1303 [](sdbusplus::message::message& message) {
1304 std::string intfName;
1305 std::map<std::string, std::variant<std::string>> properties;
1306
1307 message.read(intfName, properties);
1308
1309 std::variant<std::string> currentHostState;
1310
1311 try
1312 {
1313 currentHostState = properties.at("CurrentHostState");
1314 }
1315 catch (const std::out_of_range& e)
1316 {
1317 std::cerr << "Error in finding CurrentHostState property\n";
1318
1319 return;
1320 }
1321
1322 if (std::get<std::string>(currentHostState) ==
1323 "xyz.openbmc_project.State.Host.HostState.Running")
1324 {
1325 pohCounterTimerStart();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001326 // Clear the restart cause set for the next restart
1327 clearRestartCause();
Yong Li8d660212019-12-27 10:18:10 +08001328 sd_journal_send("MESSAGE=Host system DC power is on",
1329 "PRIORITY=%i", LOG_INFO,
1330 "REDFISH_MESSAGE_ID=%s",
1331 "OpenBMC.0.1.DCPowerOn", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001332 }
1333 else
1334 {
1335 pohCounterTimer.cancel();
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301336 // POST_COMPLETE GPIO event is not working in some platforms
1337 // when power state is changed to OFF. This resulted in
1338 // 'OperatingSystemState' to stay at 'Standby', even though
1339 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1340 // if HostState is trurned to OFF.
1341 osIface->set_property("OperatingSystemState",
1342 std::string("Inactive"));
1343
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001344 // Set the restart cause set for this restart
1345 setRestartCause();
Rashmi RV89f61312020-01-22 15:41:50 +05301346 resetACBootProperty();
Yong Li8d660212019-12-27 10:18:10 +08001347 sd_journal_send("MESSAGE=Host system DC power is off",
1348 "PRIORITY=%i", LOG_INFO,
1349 "REDFISH_MESSAGE_ID=%s",
1350 "OpenBMC.0.1.DCPowerOff", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001351 }
1352 });
1353}
1354
1355static void sioPowerGoodWatchdogTimerStart()
1356{
1357 std::cerr << "SIO power good watchdog timer started\n";
1358 sioPowerGoodWatchdogTimer.expires_after(
1359 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1360 sioPowerGoodWatchdogTimer.async_wait(
1361 [](const boost::system::error_code ec) {
1362 if (ec)
1363 {
1364 // operation_aborted is expected if timer is canceled before
1365 // completion.
1366 if (ec != boost::asio::error::operation_aborted)
1367 {
1368 std::cerr << "SIO power good watchdog async_wait failed: "
1369 << ec.message() << "\n";
1370 }
1371 std::cerr << "SIO power good watchdog timer canceled\n";
1372 return;
1373 }
1374 std::cerr << "SIO power good watchdog timer completed\n";
1375 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1376 });
1377}
1378
1379static void powerStateOn(const Event event)
1380{
1381 logEvent(__FUNCTION__, event);
1382 switch (event)
1383 {
1384 case Event::psPowerOKDeAssert:
1385 setPowerState(PowerState::off);
1386 // DC power is unexpectedly lost, beep
1387 beep(beepPowerFail);
1388 break;
1389 case Event::sioS5Assert:
1390 setPowerState(PowerState::transitionToOff);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001391 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001392 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001393 case Event::postCompleteDeAssert:
1394 setPowerState(PowerState::checkForWarmReset);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001395 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001396 warmResetCheckTimerStart();
1397 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001398 case Event::powerButtonPressed:
1399 setPowerState(PowerState::gracefulTransitionToOff);
1400 gracefulPowerOffTimerStart();
1401 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001402 case Event::resetButtonPressed:
1403 setPowerState(PowerState::checkForWarmReset);
1404 warmResetCheckTimerStart();
1405 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001406 case Event::powerOffRequest:
1407 setPowerState(PowerState::transitionToOff);
1408 forcePowerOff();
1409 break;
1410 case Event::gracefulPowerOffRequest:
1411 setPowerState(PowerState::gracefulTransitionToOff);
1412 gracefulPowerOffTimerStart();
1413 gracefulPowerOff();
1414 break;
1415 case Event::powerCycleRequest:
1416 setPowerState(PowerState::transitionToCycleOff);
1417 forcePowerOff();
1418 break;
1419 case Event::gracefulPowerCycleRequest:
1420 setPowerState(PowerState::gracefulTransitionToCycleOff);
1421 gracefulPowerOffTimerStart();
1422 gracefulPowerOff();
1423 break;
1424 case Event::resetRequest:
1425 reset();
1426 break;
1427 default:
1428 std::cerr << "No action taken.\n";
1429 break;
1430 }
1431}
1432
1433static void powerStateWaitForPSPowerOK(const Event event)
1434{
1435 logEvent(__FUNCTION__, event);
1436 switch (event)
1437 {
1438 case Event::psPowerOKAssert:
1439 // Cancel any GPIO assertions held during the transition
1440 gpioAssertTimer.cancel();
1441 psPowerOKWatchdogTimer.cancel();
1442 sioPowerGoodWatchdogTimerStart();
1443 setPowerState(PowerState::waitForSIOPowerGood);
1444 break;
1445 case Event::psPowerOKWatchdogTimerExpired:
1446 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001447 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001448 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001449 case Event::sioPowerGoodAssert:
1450 psPowerOKWatchdogTimer.cancel();
1451 setPowerState(PowerState::on);
1452 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001453 default:
1454 std::cerr << "No action taken.\n";
1455 break;
1456 }
1457}
1458
1459static void powerStateWaitForSIOPowerGood(const Event event)
1460{
1461 logEvent(__FUNCTION__, event);
1462 switch (event)
1463 {
1464 case Event::sioPowerGoodAssert:
1465 sioPowerGoodWatchdogTimer.cancel();
1466 setPowerState(PowerState::on);
1467 break;
1468 case Event::sioPowerGoodWatchdogTimerExpired:
1469 setPowerState(PowerState::failedTransitionToOn);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001470 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001471 forcePowerOff();
1472 break;
1473 default:
1474 std::cerr << "No action taken.\n";
1475 break;
1476 }
1477}
1478
1479static void powerStateFailedTransitionToOn(const Event event)
1480{
1481 logEvent(__FUNCTION__, event);
1482 switch (event)
1483 {
1484 case Event::psPowerOKAssert:
1485 // We're in a failure state, so don't allow the system to turn on
1486 // without a user request
1487 forcePowerOff();
1488 break;
1489 case Event::psPowerOKDeAssert:
1490 // Cancel any GPIO assertions held during the transition
1491 gpioAssertTimer.cancel();
1492 break;
1493 case Event::powerButtonPressed:
1494 psPowerOKWatchdogTimerStart();
1495 setPowerState(PowerState::waitForPSPowerOK);
1496 break;
1497 case Event::powerOnRequest:
1498 psPowerOKWatchdogTimerStart();
1499 setPowerState(PowerState::waitForPSPowerOK);
1500 powerOn();
1501 break;
1502 default:
1503 std::cerr << "No action taken.\n";
1504 break;
1505 }
1506}
1507
1508static void powerStateOff(const Event event)
1509{
1510 logEvent(__FUNCTION__, event);
1511 switch (event)
1512 {
1513 case Event::psPowerOKAssert:
1514 setPowerState(PowerState::waitForSIOPowerGood);
1515 break;
1516 case Event::sioS5DeAssert:
1517 setPowerState(PowerState::waitForPSPowerOK);
1518 break;
1519 case Event::powerButtonPressed:
1520 psPowerOKWatchdogTimerStart();
1521 setPowerState(PowerState::waitForPSPowerOK);
1522 break;
1523 case Event::powerOnRequest:
1524 psPowerOKWatchdogTimerStart();
1525 setPowerState(PowerState::waitForPSPowerOK);
1526 powerOn();
1527 break;
1528 default:
1529 std::cerr << "No action taken.\n";
1530 break;
1531 }
1532}
1533
1534static void powerStateTransitionToOff(const Event event)
1535{
1536 logEvent(__FUNCTION__, event);
1537 switch (event)
1538 {
1539 case Event::psPowerOKDeAssert:
1540 // Cancel any GPIO assertions held during the transition
1541 gpioAssertTimer.cancel();
1542 setPowerState(PowerState::off);
1543 break;
1544 default:
1545 std::cerr << "No action taken.\n";
1546 break;
1547 }
1548}
1549
1550static void powerStateGracefulTransitionToOff(const Event event)
1551{
1552 logEvent(__FUNCTION__, event);
1553 switch (event)
1554 {
1555 case Event::psPowerOKDeAssert:
1556 gracefulPowerOffTimer.cancel();
1557 setPowerState(PowerState::off);
1558 break;
1559 case Event::gracefulPowerOffTimerExpired:
1560 setPowerState(PowerState::on);
1561 break;
1562 default:
1563 std::cerr << "No action taken.\n";
1564 break;
1565 }
1566}
1567
1568static void powerStateCycleOff(const Event event)
1569{
1570 logEvent(__FUNCTION__, event);
1571 switch (event)
1572 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001573 case Event::psPowerOKAssert:
1574 powerCycleTimer.cancel();
1575 setPowerState(PowerState::waitForSIOPowerGood);
1576 break;
1577 case Event::sioS5DeAssert:
1578 powerCycleTimer.cancel();
1579 setPowerState(PowerState::waitForPSPowerOK);
1580 break;
1581 case Event::powerButtonPressed:
1582 powerCycleTimer.cancel();
1583 psPowerOKWatchdogTimerStart();
1584 setPowerState(PowerState::waitForPSPowerOK);
1585 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001586 case Event::powerCycleTimerExpired:
1587 psPowerOKWatchdogTimerStart();
1588 setPowerState(PowerState::waitForPSPowerOK);
1589 powerOn();
1590 break;
1591 default:
1592 std::cerr << "No action taken.\n";
1593 break;
1594 }
1595}
1596
1597static void powerStateTransitionToCycleOff(const Event event)
1598{
1599 logEvent(__FUNCTION__, event);
1600 switch (event)
1601 {
1602 case Event::psPowerOKDeAssert:
1603 // Cancel any GPIO assertions held during the transition
1604 gpioAssertTimer.cancel();
1605 setPowerState(PowerState::cycleOff);
1606 powerCycleTimerStart();
1607 break;
1608 default:
1609 std::cerr << "No action taken.\n";
1610 break;
1611 }
1612}
1613
1614static void powerStateGracefulTransitionToCycleOff(const Event event)
1615{
1616 logEvent(__FUNCTION__, event);
1617 switch (event)
1618 {
1619 case Event::psPowerOKDeAssert:
1620 gracefulPowerOffTimer.cancel();
1621 setPowerState(PowerState::cycleOff);
1622 powerCycleTimerStart();
1623 break;
1624 case Event::gracefulPowerOffTimerExpired:
1625 setPowerState(PowerState::on);
1626 break;
1627 default:
1628 std::cerr << "No action taken.\n";
1629 break;
1630 }
1631}
1632
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001633static void powerStateCheckForWarmReset(const Event event)
1634{
1635 logEvent(__FUNCTION__, event);
1636 switch (event)
1637 {
1638 case Event::sioS5Assert:
1639 warmResetCheckTimer.cancel();
1640 setPowerState(PowerState::transitionToOff);
1641 break;
1642 case Event::warmResetDetected:
1643 setPowerState(PowerState::on);
1644 break;
P.K. Lee344dae82019-11-27 16:35:05 +08001645 case Event::psPowerOKDeAssert:
1646 warmResetCheckTimer.cancel();
1647 setPowerState(PowerState::off);
1648 // DC power is unexpectedly lost, beep
1649 beep(beepPowerFail);
1650 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001651 default:
1652 std::cerr << "No action taken.\n";
1653 break;
1654 }
1655}
1656
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001657static void psPowerOKHandler()
1658{
1659 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1660
1661 Event powerControlEvent =
1662 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1663 ? Event::psPowerOKAssert
1664 : Event::psPowerOKDeAssert;
1665
1666 sendPowerControlEvent(powerControlEvent);
1667 psPowerOKEvent.async_wait(
1668 boost::asio::posix::stream_descriptor::wait_read,
1669 [](const boost::system::error_code ec) {
1670 if (ec)
1671 {
1672 std::cerr << "power supply power OK handler error: "
1673 << ec.message() << "\n";
1674 return;
1675 }
1676 psPowerOKHandler();
1677 });
1678}
1679
1680static void sioPowerGoodHandler()
1681{
1682 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1683
1684 Event powerControlEvent =
1685 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1686 ? Event::sioPowerGoodAssert
1687 : Event::sioPowerGoodDeAssert;
1688
1689 sendPowerControlEvent(powerControlEvent);
1690 sioPowerGoodEvent.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 power good handler error: " << ec.message()
1696 << "\n";
1697 return;
1698 }
1699 sioPowerGoodHandler();
1700 });
1701}
1702
1703static void sioOnControlHandler()
1704{
1705 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1706
1707 bool sioOnControl =
1708 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1709 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1710 sioOnControlEvent.async_wait(
1711 boost::asio::posix::stream_descriptor::wait_read,
1712 [](const boost::system::error_code ec) {
1713 if (ec)
1714 {
1715 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1716 << "\n";
1717 return;
1718 }
1719 sioOnControlHandler();
1720 });
1721}
1722
1723static void sioS5Handler()
1724{
1725 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1726
1727 Event powerControlEvent =
1728 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1729 ? Event::sioS5Assert
1730 : Event::sioS5DeAssert;
1731
1732 sendPowerControlEvent(powerControlEvent);
1733 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1734 [](const boost::system::error_code ec) {
1735 if (ec)
1736 {
1737 std::cerr << "SIO S5 handler error: "
1738 << ec.message() << "\n";
1739 return;
1740 }
1741 sioS5Handler();
1742 });
1743}
1744
1745static void powerButtonHandler()
1746{
1747 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1748
1749 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1750 {
1751 powerButtonPressLog();
1752 powerButtonIface->set_property("ButtonPressed", true);
1753 if (!powerButtonMask)
1754 {
1755 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001756 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001757 }
1758 else
1759 {
1760 std::cerr << "power button press masked\n";
1761 }
1762 }
1763 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1764 {
1765 powerButtonIface->set_property("ButtonPressed", false);
1766 }
1767 powerButtonEvent.async_wait(
1768 boost::asio::posix::stream_descriptor::wait_read,
1769 [](const boost::system::error_code ec) {
1770 if (ec)
1771 {
1772 std::cerr << "power button handler error: " << ec.message()
1773 << "\n";
1774 return;
1775 }
1776 powerButtonHandler();
1777 });
1778}
1779
1780static void resetButtonHandler()
1781{
1782 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1783
1784 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1785 {
1786 resetButtonPressLog();
1787 resetButtonIface->set_property("ButtonPressed", true);
1788 if (!resetButtonMask)
1789 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001790 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001791 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001792 }
1793 else
1794 {
1795 std::cerr << "reset button press masked\n";
1796 }
1797 }
1798 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1799 {
1800 resetButtonIface->set_property("ButtonPressed", false);
1801 }
1802 resetButtonEvent.async_wait(
1803 boost::asio::posix::stream_descriptor::wait_read,
1804 [](const boost::system::error_code ec) {
1805 if (ec)
1806 {
1807 std::cerr << "reset button handler error: " << ec.message()
1808 << "\n";
1809 return;
1810 }
1811 resetButtonHandler();
1812 });
1813}
1814
1815static void nmiSetEnablePorperty(bool value)
1816{
1817 conn->async_method_call(
1818 [](boost::system::error_code ec) {
1819 if (ec)
1820 {
1821 std::cerr << "failed to set NMI source\n";
1822 }
1823 },
Chen Yugang303bd582019-11-01 08:45:06 +08001824 "xyz.openbmc_project.Settings",
1825 "/xyz/openbmc_project/Chassis/Control/NMISource",
1826 "org.freedesktop.DBus.Properties", "Set",
1827 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
1828 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001829}
1830
1831static void nmiReset(void)
1832{
1833 static constexpr const uint8_t value = 1;
1834 const static constexpr int nmiOutPulseTimeMs = 200;
1835
1836 std::cerr << "NMI out action \n";
1837 nmiOutLine.set_value(value);
1838 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1839 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1840 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1841 // restore the NMI_OUT GPIO line back to the opposite value
1842 nmiOutLine.set_value(!value);
1843 std::cerr << nmiOutName << " released\n";
1844 if (ec)
1845 {
1846 // operation_aborted is expected if timer is canceled before
1847 // completion.
1848 if (ec != boost::asio::error::operation_aborted)
1849 {
1850 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1851 << "\n";
1852 }
1853 }
1854 });
1855 // log to redfish
1856 nmiDiagIntLog();
1857 std::cerr << "NMI out action completed\n";
1858 // reset Enable Property
1859 nmiSetEnablePorperty(false);
1860}
1861
1862static void nmiSourcePropertyMonitor(void)
1863{
1864 std::cerr << " NMI Source Property Monitor \n";
1865
1866 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1867 std::make_unique<sdbusplus::bus::match::match>(
1868 *conn,
1869 "type='signal',interface='org.freedesktop.DBus.Properties',"
Chen Yugang303bd582019-11-01 08:45:06 +08001870 "member='PropertiesChanged',arg0namespace='xyz.openbmc_project."
1871 "Chassis.Control."
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001872 "NMISource'",
1873 [](sdbusplus::message::message& msg) {
1874 std::string interfaceName;
1875 boost::container::flat_map<std::string,
1876 std::variant<bool, std::string>>
1877 propertiesChanged;
1878 std::string state;
1879 bool value = true;
1880 try
1881 {
1882 msg.read(interfaceName, propertiesChanged);
1883 if (propertiesChanged.begin()->first == "Enabled")
1884 {
1885 value =
1886 std::get<bool>(propertiesChanged.begin()->second);
1887 std::cerr
1888 << " NMI Enabled propertiesChanged value: " << value
1889 << "\n";
1890 nmiEnabled = value;
1891 if (nmiEnabled)
1892 {
1893 nmiReset();
1894 }
1895 }
1896 }
1897 catch (std::exception& e)
1898 {
1899 std::cerr << "Unable to read NMI source\n";
1900 return;
1901 }
1902 });
1903}
1904
1905static void setNmiSource()
1906{
1907 conn->async_method_call(
1908 [](boost::system::error_code ec) {
1909 if (ec)
1910 {
1911 std::cerr << "failed to set NMI source\n";
1912 }
1913 },
Chen Yugang303bd582019-11-01 08:45:06 +08001914 "xyz.openbmc_project.Settings",
1915 "/xyz/openbmc_project/Chassis/Control/NMISource",
1916 "org.freedesktop.DBus.Properties", "Set",
1917 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
1918 std::variant<std::string>{"xyz.openbmc_project.Chassis.Control."
1919 "NMISource.BMCSourceSignal.FpBtn"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001920 // set Enable Property
1921 nmiSetEnablePorperty(true);
1922}
1923
1924static void nmiButtonHandler()
1925{
1926 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1927
1928 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1929 {
1930 nmiButtonPressLog();
1931 nmiButtonIface->set_property("ButtonPressed", true);
1932 if (nmiButtonMasked)
1933 {
1934 std::cerr << "NMI button press masked\n";
1935 }
1936 else
1937 {
1938 setNmiSource();
1939 }
1940 }
1941 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1942 {
1943 nmiButtonIface->set_property("ButtonPressed", false);
1944 }
1945 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1946 [](const boost::system::error_code ec) {
1947 if (ec)
1948 {
1949 std::cerr << "NMI button handler error: "
1950 << ec.message() << "\n";
1951 return;
1952 }
1953 nmiButtonHandler();
1954 });
1955}
1956
1957static void idButtonHandler()
1958{
1959 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1960
1961 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1962 {
1963 idButtonIface->set_property("ButtonPressed", true);
1964 }
1965 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1966 {
1967 idButtonIface->set_property("ButtonPressed", false);
1968 }
1969 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1970 [](const boost::system::error_code& ec) {
1971 if (ec)
1972 {
1973 std::cerr << "ID button handler error: "
1974 << ec.message() << "\n";
1975 return;
1976 }
1977 idButtonHandler();
1978 });
1979}
1980
1981static void postCompleteHandler()
1982{
1983 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
1984
1985 bool postComplete =
1986 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001987 if (postComplete)
1988 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001989 sendPowerControlEvent(Event::postCompleteAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001990 osIface->set_property("OperatingSystemState", std::string("Standby"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001991 }
1992 else
1993 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001994 sendPowerControlEvent(Event::postCompleteDeAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001995 osIface->set_property("OperatingSystemState", std::string("Inactive"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001996 }
1997 postCompleteEvent.async_wait(
1998 boost::asio::posix::stream_descriptor::wait_read,
1999 [](const boost::system::error_code ec) {
2000 if (ec)
2001 {
2002 std::cerr << "POST complete handler error: " << ec.message()
2003 << "\n";
2004 return;
2005 }
2006 postCompleteHandler();
2007 });
2008}
2009} // namespace power_control
2010
2011int main(int argc, char* argv[])
2012{
2013 std::cerr << "Start Chassis power control service...\n";
2014 power_control::conn =
2015 std::make_shared<sdbusplus::asio::connection>(power_control::io);
2016
2017 // Request all the dbus names
2018 power_control::conn->request_name("xyz.openbmc_project.State.Host");
2019 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
2020 power_control::conn->request_name(
2021 "xyz.openbmc_project.State.OperatingSystem");
2022 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
Chen Yugang174ec662019-08-19 19:58:49 +08002023 power_control::conn->request_name("xyz.openbmc_project.Control.Host.NMI");
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002024 power_control::conn->request_name(
2025 "xyz.openbmc_project.Control.Host.RestartCause");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002026
2027 // Request PS_PWROK GPIO events
2028 if (!power_control::requestGPIOEvents(
2029 "PS_PWROK", power_control::psPowerOKHandler,
2030 power_control::psPowerOKLine, power_control::psPowerOKEvent))
2031 {
2032 return -1;
2033 }
2034
2035 // Request SIO_POWER_GOOD GPIO events
2036 if (!power_control::requestGPIOEvents(
2037 "SIO_POWER_GOOD", power_control::sioPowerGoodHandler,
2038 power_control::sioPowerGoodLine, power_control::sioPowerGoodEvent))
2039 {
2040 return -1;
2041 }
2042
2043 // Request SIO_ONCONTROL GPIO events
2044 if (!power_control::requestGPIOEvents(
2045 "SIO_ONCONTROL", power_control::sioOnControlHandler,
2046 power_control::sioOnControlLine, power_control::sioOnControlEvent))
2047 {
2048 return -1;
2049 }
2050
2051 // Request SIO_S5 GPIO events
2052 if (!power_control::requestGPIOEvents("SIO_S5", power_control::sioS5Handler,
2053 power_control::sioS5Line,
2054 power_control::sioS5Event))
2055 {
2056 return -1;
2057 }
2058
2059 // Request POWER_BUTTON GPIO events
2060 if (!power_control::requestGPIOEvents(
2061 "POWER_BUTTON", power_control::powerButtonHandler,
2062 power_control::powerButtonLine, power_control::powerButtonEvent))
2063 {
2064 return -1;
2065 }
2066
2067 // Request RESET_BUTTON GPIO events
2068 if (!power_control::requestGPIOEvents(
2069 "RESET_BUTTON", power_control::resetButtonHandler,
2070 power_control::resetButtonLine, power_control::resetButtonEvent))
2071 {
2072 return -1;
2073 }
2074
2075 // Request NMI_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08002076 power_control::requestGPIOEvents(
2077 "NMI_BUTTON", power_control::nmiButtonHandler,
2078 power_control::nmiButtonLine, power_control::nmiButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002079
2080 // Request ID_BUTTON GPIO events
Vijay Khemka33a532d2019-11-14 16:50:35 -08002081 power_control::requestGPIOEvents(
2082 "ID_BUTTON", power_control::idButtonHandler,
2083 power_control::idButtonLine, power_control::idButtonEvent);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002084
2085 // Request POST_COMPLETE GPIO events
2086 if (!power_control::requestGPIOEvents(
2087 "POST_COMPLETE", power_control::postCompleteHandler,
2088 power_control::postCompleteLine, power_control::postCompleteEvent))
2089 {
2090 return -1;
2091 }
2092
2093 // initialize NMI_OUT GPIO.
Vijay Khemka33a532d2019-11-14 16:50:35 -08002094 power_control::setGPIOOutput(power_control::nmiOutName, 0,
2095 power_control::nmiOutLine);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002096
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002097 // Initialize POWER_OUT and RESET_OUT GPIO.
2098 gpiod::line line;
2099 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
2100 {
2101 return -1;
2102 }
2103
2104 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
2105 {
2106 return -1;
2107 }
2108
2109 // Release line
2110 line.reset();
2111
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002112 // Initialize the power state
2113 power_control::powerState = power_control::PowerState::off;
2114 // Check power good
2115 if (power_control::psPowerOKLine.get_value() > 0)
2116 {
2117 power_control::powerState = power_control::PowerState::on;
2118 }
2119
2120 // Initialize the power state storage
2121 if (power_control::initializePowerStateStorage() < 0)
2122 {
2123 return -1;
2124 }
2125
2126 // Check if we need to start the Power Restore policy
2127 power_control::powerRestorePolicyCheck();
2128
Vijay Khemka33a532d2019-11-14 16:50:35 -08002129 if (power_control::nmiOutLine)
2130 power_control::nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002131
2132 std::cerr << "Initializing power state. ";
2133 power_control::logStateTransition(power_control::powerState);
2134
2135 // Power Control Service
2136 sdbusplus::asio::object_server hostServer =
2137 sdbusplus::asio::object_server(power_control::conn);
2138
2139 // Power Control Interface
2140 power_control::hostIface = hostServer.add_interface(
2141 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
2142
2143 power_control::hostIface->register_property(
2144 "RequestedHostTransition",
2145 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2146 [](const std::string& requested, std::string& resp) {
2147 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2148 {
2149 sendPowerControlEvent(
2150 power_control::Event::gracefulPowerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002151 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002152 }
2153 else if (requested ==
2154 "xyz.openbmc_project.State.Host.Transition.On")
2155 {
2156 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002157 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002158 }
2159 else if (requested ==
2160 "xyz.openbmc_project.State.Host.Transition.Reboot")
2161 {
Jason M. Billse7520ba2020-01-31 11:19:03 -08002162 sendPowerControlEvent(power_control::Event::powerCycleRequest);
2163 addRestartCause(power_control::RestartCause::command);
2164 }
2165 else if (requested == "xyz.openbmc_project.State.Host.Transition."
2166 "GracefulWarmReboot")
2167 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002168 sendPowerControlEvent(
2169 power_control::Event::gracefulPowerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002170 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002171 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08002172 else if (requested == "xyz.openbmc_project.State.Host.Transition."
2173 "ForceWarmReboot")
2174 {
2175 sendPowerControlEvent(power_control::Event::resetRequest);
2176 addRestartCause(power_control::RestartCause::command);
2177 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002178 else
2179 {
2180 std::cerr << "Unrecognized host state transition request.\n";
2181 throw std::invalid_argument("Unrecognized Transition Request");
2182 return 0;
2183 }
2184 resp = requested;
2185 return 1;
2186 });
2187 power_control::hostIface->register_property(
2188 "CurrentHostState",
2189 std::string(power_control::getHostState(power_control::powerState)));
2190
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002191 power_control::hostIface->initialize();
2192
2193 // Chassis Control Service
2194 sdbusplus::asio::object_server chassisServer =
2195 sdbusplus::asio::object_server(power_control::conn);
2196
2197 // Chassis Control Interface
2198 power_control::chassisIface =
2199 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
2200 "xyz.openbmc_project.State.Chassis");
2201
2202 power_control::chassisIface->register_property(
2203 "RequestedPowerTransition",
2204 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2205 [](const std::string& requested, std::string& resp) {
2206 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2207 {
2208 sendPowerControlEvent(power_control::Event::powerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002209 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002210 }
2211 else if (requested ==
2212 "xyz.openbmc_project.State.Chassis.Transition.On")
2213 {
2214 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002215 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002216 }
2217 else if (requested ==
2218 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2219 {
2220 sendPowerControlEvent(power_control::Event::powerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002221 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002222 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002223 else
2224 {
2225 std::cerr << "Unrecognized chassis state transition request.\n";
2226 throw std::invalid_argument("Unrecognized Transition Request");
2227 return 0;
2228 }
2229 resp = requested;
2230 return 1;
2231 });
2232 power_control::chassisIface->register_property(
2233 "CurrentPowerState",
2234 std::string(power_control::getChassisState(power_control::powerState)));
2235 power_control::chassisIface->register_property(
2236 "LastStateChangeTime", power_control::getCurrentTimeMs());
2237
2238 power_control::chassisIface->initialize();
2239
2240 // Buttons Service
2241 sdbusplus::asio::object_server buttonsServer =
2242 sdbusplus::asio::object_server(power_control::conn);
2243
2244 // Power Button Interface
2245 power_control::powerButtonIface = buttonsServer.add_interface(
2246 "/xyz/openbmc_project/chassis/buttons/power",
2247 "xyz.openbmc_project.Chassis.Buttons");
2248
2249 power_control::powerButtonIface->register_property(
2250 "ButtonMasked", false, [](const bool requested, bool& current) {
2251 if (requested)
2252 {
2253 if (power_control::powerButtonMask)
2254 {
2255 return 1;
2256 }
2257 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002258 power_control::powerOutName, 1,
2259 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002260 {
2261 throw std::runtime_error("Failed to request GPIO");
2262 return 0;
2263 }
2264 std::cerr << "Power Button Masked.\n";
2265 }
2266 else
2267 {
2268 if (!power_control::powerButtonMask)
2269 {
2270 return 1;
2271 }
2272 std::cerr << "Power Button Un-masked\n";
2273 power_control::powerButtonMask.reset();
2274 }
2275 // Update the mask setting
2276 current = requested;
2277 return 1;
2278 });
2279
2280 // Check power button state
2281 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2282 power_control::powerButtonIface->register_property("ButtonPressed",
2283 powerButtonPressed);
2284
2285 power_control::powerButtonIface->initialize();
2286
2287 // Reset Button Interface
2288 power_control::resetButtonIface = buttonsServer.add_interface(
2289 "/xyz/openbmc_project/chassis/buttons/reset",
2290 "xyz.openbmc_project.Chassis.Buttons");
2291
2292 power_control::resetButtonIface->register_property(
2293 "ButtonMasked", false, [](const bool requested, bool& current) {
2294 if (requested)
2295 {
2296 if (power_control::resetButtonMask)
2297 {
2298 return 1;
2299 }
2300 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002301 power_control::resetOutName, 1,
2302 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002303 {
2304 throw std::runtime_error("Failed to request GPIO");
2305 return 0;
2306 }
2307 std::cerr << "Reset Button Masked.\n";
2308 }
2309 else
2310 {
2311 if (!power_control::resetButtonMask)
2312 {
2313 return 1;
2314 }
2315 std::cerr << "Reset Button Un-masked\n";
2316 power_control::resetButtonMask.reset();
2317 }
2318 // Update the mask setting
2319 current = requested;
2320 return 1;
2321 });
2322
2323 // Check reset button state
2324 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2325 power_control::resetButtonIface->register_property("ButtonPressed",
2326 resetButtonPressed);
2327
2328 power_control::resetButtonIface->initialize();
2329
Vijay Khemka33a532d2019-11-14 16:50:35 -08002330 if (power_control::nmiButtonLine)
2331 {
2332 // NMI Button Interface
2333 power_control::nmiButtonIface = buttonsServer.add_interface(
2334 "/xyz/openbmc_project/chassis/buttons/nmi",
2335 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002336
Vijay Khemka33a532d2019-11-14 16:50:35 -08002337 power_control::nmiButtonIface->register_property(
2338 "ButtonMasked", false, [](const bool requested, bool& current) {
2339 if (power_control::nmiButtonMasked == requested)
2340 {
2341 // NMI button mask is already set as requested, so no change
2342 return 1;
2343 }
2344 if (requested)
2345 {
2346 std::cerr << "NMI Button Masked.\n";
2347 power_control::nmiButtonMasked = true;
2348 }
2349 else
2350 {
2351 std::cerr << "NMI Button Un-masked.\n";
2352 power_control::nmiButtonMasked = false;
2353 }
2354 // Update the mask setting
2355 current = power_control::nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002356 return 1;
Vijay Khemka33a532d2019-11-14 16:50:35 -08002357 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002358
Vijay Khemka33a532d2019-11-14 16:50:35 -08002359 // Check NMI button state
2360 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2361 power_control::nmiButtonIface->register_property("ButtonPressed",
2362 nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002363
Vijay Khemka33a532d2019-11-14 16:50:35 -08002364 power_control::nmiButtonIface->initialize();
2365 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002366
Vijay Khemka33a532d2019-11-14 16:50:35 -08002367 if (power_control::nmiOutLine)
2368 {
2369 // NMI out Service
2370 sdbusplus::asio::object_server nmiOutServer =
2371 sdbusplus::asio::object_server(power_control::conn);
Chen Yugang174ec662019-08-19 19:58:49 +08002372
Vijay Khemka33a532d2019-11-14 16:50:35 -08002373 // NMI out Interface
2374 power_control::nmiOutIface =
2375 nmiOutServer.add_interface("/xyz/openbmc_project/control/host0/nmi",
2376 "xyz.openbmc_project.Control.Host.NMI");
2377 power_control::nmiOutIface->register_method("NMI",
2378 power_control::nmiReset);
2379 power_control::nmiOutIface->initialize();
2380 }
Chen Yugang174ec662019-08-19 19:58:49 +08002381
Vijay Khemka33a532d2019-11-14 16:50:35 -08002382 if (power_control::idButtonLine)
2383 {
2384 // ID Button Interface
2385 power_control::idButtonIface = buttonsServer.add_interface(
2386 "/xyz/openbmc_project/chassis/buttons/id",
2387 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002388
Vijay Khemka33a532d2019-11-14 16:50:35 -08002389 // Check ID button state
2390 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2391 power_control::idButtonIface->register_property("ButtonPressed",
2392 idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002393
Vijay Khemka33a532d2019-11-14 16:50:35 -08002394 power_control::idButtonIface->initialize();
2395 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002396
2397 // OS State Service
2398 sdbusplus::asio::object_server osServer =
2399 sdbusplus::asio::object_server(power_control::conn);
2400
2401 // OS State Interface
2402 power_control::osIface = osServer.add_interface(
2403 "/xyz/openbmc_project/state/os",
2404 "xyz.openbmc_project.State.OperatingSystem.Status");
2405
2406 // Get the initial OS state based on POST complete
2407 // 0: Asserted, OS state is "Standby" (ready to boot)
2408 // 1: De-Asserted, OS state is "Inactive"
2409 std::string osState = power_control::postCompleteLine.get_value() > 0
2410 ? "Inactive"
2411 : "Standby";
2412
2413 power_control::osIface->register_property("OperatingSystemState",
2414 std::string(osState));
2415
2416 power_control::osIface->initialize();
2417
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002418 // Restart Cause Service
2419 sdbusplus::asio::object_server restartCauseServer =
2420 sdbusplus::asio::object_server(power_control::conn);
2421
2422 // Restart Cause Interface
2423 power_control::restartCauseIface = restartCauseServer.add_interface(
2424 "/xyz/openbmc_project/control/host0/restart_cause",
2425 "xyz.openbmc_project.Control.Host.RestartCause");
2426
2427 power_control::restartCauseIface->register_property(
2428 "RestartCause",
2429 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
2430
2431 power_control::restartCauseIface->register_property(
2432 "RequestedRestartCause",
2433 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
2434 [](const std::string& requested, std::string& resp) {
2435 if (requested ==
2436 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
2437 {
2438 power_control::addRestartCause(
2439 power_control::RestartCause::watchdog);
2440 }
2441 else
2442 {
2443 throw std::invalid_argument(
2444 "Unrecognized RestartCause Request");
2445 return 0;
2446 }
2447
2448 std::cerr << "RestartCause requested: " << requested << "\n";
2449 resp = requested;
2450 return 1;
2451 });
2452
2453 power_control::restartCauseIface->initialize();
2454
Yong Li8d660212019-12-27 10:18:10 +08002455 power_control::currentHostStateMonitor();
2456
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002457 power_control::io.run();
2458
2459 return 0;
2460}