blob: 2f048852969ccaf4a0eafe193adc097bdad5b719 [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
Jason M. Billse63dea02020-08-27 12:07:35 -070021#include <boost/asio/io_service.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070022#include <boost/asio/posix/stream_descriptor.hpp>
Jason M. Billse63dea02020-08-27 12:07:35 -070023#include <boost/asio/steady_timer.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070024#include <boost/container/flat_map.hpp>
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070025#include <boost/container/flat_set.hpp>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070026#include <gpiod.hpp>
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053027#include <nlohmann/json.hpp>
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>
Vijay Khemka2b6f4422020-05-29 11:13:23 -070030
31#include <filesystem>
32#include <fstream>
33#include <iostream>
Ed Tanousf61ca6f2019-08-15 15:09:05 -070034#include <string_view>
35
36namespace power_control
37{
38static boost::asio::io_service io;
39std::shared_ptr<sdbusplus::asio::connection> conn;
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +053040
41static std::string node = "0";
42
43static std::string powerOutName;
44static std::string powerOkName;
45static std::string resetOutName;
46static std::string nmiOutName;
47static std::string sioPwrGoodName;
48static std::string sioOnControlName;
49static std::string sioS5Name;
50static std::string postCompleteName;
51static std::string powerButtonName;
52static std::string resetButtonName;
53static std::string idButtonName;
54static std::string nmiButtonName;
55
Ed Tanousf61ca6f2019-08-15 15:09:05 -070056static std::shared_ptr<sdbusplus::asio::dbus_interface> hostIface;
57static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisIface;
Vijay Khemka04175c22020-10-09 14:28:11 -070058#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -070059static std::shared_ptr<sdbusplus::asio::dbus_interface> chassisSysIface;
Vijay Khemka04175c22020-10-09 14:28:11 -070060#endif
Ed Tanousf61ca6f2019-08-15 15:09:05 -070061static std::shared_ptr<sdbusplus::asio::dbus_interface> powerButtonIface;
62static std::shared_ptr<sdbusplus::asio::dbus_interface> resetButtonIface;
63static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiButtonIface;
64static std::shared_ptr<sdbusplus::asio::dbus_interface> osIface;
65static std::shared_ptr<sdbusplus::asio::dbus_interface> idButtonIface;
Chen Yugang174ec662019-08-19 19:58:49 +080066static std::shared_ptr<sdbusplus::asio::dbus_interface> nmiOutIface;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -070067static std::shared_ptr<sdbusplus::asio::dbus_interface> restartCauseIface;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070068
69static gpiod::line powerButtonMask;
70static gpiod::line resetButtonMask;
71static bool nmiButtonMasked = false;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070072
73const static constexpr int powerPulseTimeMs = 200;
74const static constexpr int forceOffPulseTimeMs = 15000;
75const static constexpr int resetPulseTimeMs = 500;
Jason M. Billsfc9408a2020-01-31 14:54:17 -080076const static constexpr int powerCycleTimeMs = 5000;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070077const static constexpr int sioPowerGoodWatchdogTimeMs = 1000;
78const static constexpr int psPowerOKWatchdogTimeMs = 8000;
79const static constexpr int gracefulPowerOffTimeMs = 60000;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -070080const static constexpr int warmResetCheckTimeMs = 500;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070081const static constexpr int buttonMaskTimeMs = 60000;
82const static constexpr int powerOffSaveTimeMs = 7000;
83
84const static std::filesystem::path powerControlDir = "/var/lib/power-control";
85const static constexpr std::string_view powerStateFile = "power-state";
86
87static bool nmiEnabled = true;
Ed Tanousf61ca6f2019-08-15 15:09:05 -070088
89// Timers
90// Time holding GPIOs asserted
91static boost::asio::steady_timer gpioAssertTimer(io);
92// Time between off and on during a power cycle
93static boost::asio::steady_timer powerCycleTimer(io);
94// Time OS gracefully powering off
95static boost::asio::steady_timer gracefulPowerOffTimer(io);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -070096// Time the warm reset check
97static boost::asio::steady_timer warmResetCheckTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -070098// Time power supply power OK assertion on power-on
99static boost::asio::steady_timer psPowerOKWatchdogTimer(io);
100// Time SIO power good assertion on power-on
101static boost::asio::steady_timer sioPowerGoodWatchdogTimer(io);
102// Time power-off state save for power loss tracking
103static boost::asio::steady_timer powerStateSaveTimer(io);
104// POH timer
105static boost::asio::steady_timer pohCounterTimer(io);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700106// Time when to allow restart cause updates
107static boost::asio::steady_timer restartCauseTimer(io);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700108
109// GPIO Lines and Event Descriptors
110static gpiod::line psPowerOKLine;
111static boost::asio::posix::stream_descriptor psPowerOKEvent(io);
112static gpiod::line sioPowerGoodLine;
113static boost::asio::posix::stream_descriptor sioPowerGoodEvent(io);
114static gpiod::line sioOnControlLine;
115static boost::asio::posix::stream_descriptor sioOnControlEvent(io);
116static gpiod::line sioS5Line;
117static boost::asio::posix::stream_descriptor sioS5Event(io);
118static gpiod::line powerButtonLine;
119static boost::asio::posix::stream_descriptor powerButtonEvent(io);
120static gpiod::line resetButtonLine;
121static boost::asio::posix::stream_descriptor resetButtonEvent(io);
122static gpiod::line nmiButtonLine;
123static boost::asio::posix::stream_descriptor nmiButtonEvent(io);
124static gpiod::line idButtonLine;
125static boost::asio::posix::stream_descriptor idButtonEvent(io);
126static gpiod::line postCompleteLine;
127static boost::asio::posix::stream_descriptor postCompleteEvent(io);
128static gpiod::line nmiOutLine;
129
130static constexpr uint8_t beepPowerFail = 8;
131
132static void beep(const uint8_t& beepPriority)
133{
134 std::cerr << "Beep with priority: " << (unsigned)beepPriority << "\n";
135
136 conn->async_method_call(
137 [](boost::system::error_code ec) {
138 if (ec)
139 {
140 std::cerr << "beep returned error with "
141 "async_method_call (ec = "
142 << ec << ")\n";
143 return;
144 }
145 },
146 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
147 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
148}
149
150enum class PowerState
151{
152 on,
153 waitForPSPowerOK,
154 waitForSIOPowerGood,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700155 off,
156 transitionToOff,
157 gracefulTransitionToOff,
158 cycleOff,
159 transitionToCycleOff,
160 gracefulTransitionToCycleOff,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700161 checkForWarmReset,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700162};
163static PowerState powerState;
164static std::string getPowerStateName(PowerState state)
165{
166 switch (state)
167 {
168 case PowerState::on:
169 return "On";
170 break;
171 case PowerState::waitForPSPowerOK:
172 return "Wait for Power Supply Power OK";
173 break;
174 case PowerState::waitForSIOPowerGood:
175 return "Wait for SIO Power Good";
176 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700177 case PowerState::off:
178 return "Off";
179 break;
180 case PowerState::transitionToOff:
181 return "Transition to Off";
182 break;
183 case PowerState::gracefulTransitionToOff:
184 return "Graceful Transition to Off";
185 break;
186 case PowerState::cycleOff:
187 return "Power Cycle Off";
188 break;
189 case PowerState::transitionToCycleOff:
190 return "Transition to Power Cycle Off";
191 break;
192 case PowerState::gracefulTransitionToCycleOff:
193 return "Graceful Transition to Power Cycle Off";
194 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700195 case PowerState::checkForWarmReset:
196 return "Check for Warm Reset";
197 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700198 default:
199 return "unknown state: " + std::to_string(static_cast<int>(state));
200 break;
201 }
202}
203static void logStateTransition(const PowerState state)
204{
Vijay Khemkad6c5ad12020-05-27 14:57:52 -0700205 std::string logMsg =
206 "Host0: Moving to \"" + getPowerStateName(state) + "\" state";
Vijay Khemkafc1ecc52020-04-01 10:49:28 -0700207 phosphor::logging::log<phosphor::logging::level::INFO>(
208 logMsg.c_str(),
Vijay Khemkad6c5ad12020-05-27 14:57:52 -0700209 phosphor::logging::entry("STATE=%s", getPowerStateName(state).c_str()),
210 phosphor::logging::entry("HOST=0"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700211}
212
213enum class Event
214{
215 psPowerOKAssert,
216 psPowerOKDeAssert,
217 sioPowerGoodAssert,
218 sioPowerGoodDeAssert,
219 sioS5Assert,
220 sioS5DeAssert,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700221 postCompleteAssert,
222 postCompleteDeAssert,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700223 powerButtonPressed,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700224 resetButtonPressed,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700225 powerCycleTimerExpired,
226 psPowerOKWatchdogTimerExpired,
227 sioPowerGoodWatchdogTimerExpired,
228 gracefulPowerOffTimerExpired,
229 powerOnRequest,
230 powerOffRequest,
231 powerCycleRequest,
232 resetRequest,
233 gracefulPowerOffRequest,
234 gracefulPowerCycleRequest,
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700235 warmResetDetected,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700236};
237static std::string getEventName(Event event)
238{
239 switch (event)
240 {
241 case Event::psPowerOKAssert:
242 return "power supply power OK assert";
243 break;
244 case Event::psPowerOKDeAssert:
245 return "power supply power OK de-assert";
246 break;
247 case Event::sioPowerGoodAssert:
248 return "SIO power good assert";
249 break;
250 case Event::sioPowerGoodDeAssert:
251 return "SIO power good de-assert";
252 break;
253 case Event::sioS5Assert:
254 return "SIO S5 assert";
255 break;
256 case Event::sioS5DeAssert:
257 return "SIO S5 de-assert";
258 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700259 case Event::postCompleteAssert:
260 return "POST Complete assert";
261 break;
262 case Event::postCompleteDeAssert:
263 return "POST Complete de-assert";
264 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700265 case Event::powerButtonPressed:
266 return "power button pressed";
267 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700268 case Event::resetButtonPressed:
269 return "reset button pressed";
270 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700271 case Event::powerCycleTimerExpired:
272 return "power cycle timer expired";
273 break;
274 case Event::psPowerOKWatchdogTimerExpired:
275 return "power supply power OK watchdog timer expired";
276 break;
277 case Event::sioPowerGoodWatchdogTimerExpired:
278 return "SIO power good watchdog timer expired";
279 break;
280 case Event::gracefulPowerOffTimerExpired:
281 return "graceful power-off timer expired";
282 break;
283 case Event::powerOnRequest:
284 return "power-on request";
285 break;
286 case Event::powerOffRequest:
287 return "power-off request";
288 break;
289 case Event::powerCycleRequest:
290 return "power-cycle request";
291 break;
292 case Event::resetRequest:
293 return "reset request";
294 break;
295 case Event::gracefulPowerOffRequest:
296 return "graceful power-off request";
297 break;
298 case Event::gracefulPowerCycleRequest:
299 return "graceful power-cycle request";
300 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700301 case Event::warmResetDetected:
302 return "warm reset detected";
303 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700304 default:
305 return "unknown event: " + std::to_string(static_cast<int>(event));
306 break;
307 }
308}
309static void logEvent(const std::string_view stateHandler, const Event event)
310{
Vijay Khemkafc1ecc52020-04-01 10:49:28 -0700311 std::string logMsg{stateHandler};
312 logMsg += ": " + getEventName(event) + " event received";
313 phosphor::logging::log<phosphor::logging::level::INFO>(
314 logMsg.c_str(),
315 phosphor::logging::entry("EVENT=%s", getEventName(event).c_str()));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700316}
317
318// Power state handlers
319static void powerStateOn(const Event event);
320static void powerStateWaitForPSPowerOK(const Event event);
321static void powerStateWaitForSIOPowerGood(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700322static void powerStateOff(const Event event);
323static void powerStateTransitionToOff(const Event event);
324static void powerStateGracefulTransitionToOff(const Event event);
325static void powerStateCycleOff(const Event event);
326static void powerStateTransitionToCycleOff(const Event event);
327static void powerStateGracefulTransitionToCycleOff(const Event event);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700328static void powerStateCheckForWarmReset(const Event event);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700329
330static std::function<void(const Event)> getPowerStateHandler(PowerState state)
331{
332 switch (state)
333 {
334 case PowerState::on:
335 return powerStateOn;
336 break;
337 case PowerState::waitForPSPowerOK:
338 return powerStateWaitForPSPowerOK;
339 break;
340 case PowerState::waitForSIOPowerGood:
341 return powerStateWaitForSIOPowerGood;
342 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700343 case PowerState::off:
344 return powerStateOff;
345 break;
346 case PowerState::transitionToOff:
347 return powerStateTransitionToOff;
348 break;
349 case PowerState::gracefulTransitionToOff:
350 return powerStateGracefulTransitionToOff;
351 break;
352 case PowerState::cycleOff:
353 return powerStateCycleOff;
354 break;
355 case PowerState::transitionToCycleOff:
356 return powerStateTransitionToCycleOff;
357 break;
358 case PowerState::gracefulTransitionToCycleOff:
359 return powerStateGracefulTransitionToCycleOff;
360 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700361 case PowerState::checkForWarmReset:
362 return powerStateCheckForWarmReset;
363 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700364 default:
365 return std::function<void(const Event)>{};
366 break;
367 }
368};
369
370static void sendPowerControlEvent(const Event event)
371{
372 std::function<void(const Event)> handler = getPowerStateHandler(powerState);
373 if (handler == nullptr)
374 {
375 std::cerr << "Failed to find handler for power state: "
376 << static_cast<int>(powerState) << "\n";
377 return;
378 }
379 handler(event);
380}
381
382static uint64_t getCurrentTimeMs()
383{
384 struct timespec time = {};
385
386 if (clock_gettime(CLOCK_REALTIME, &time) < 0)
387 {
388 return 0;
389 }
390 uint64_t currentTimeMs = static_cast<uint64_t>(time.tv_sec) * 1000;
391 currentTimeMs += static_cast<uint64_t>(time.tv_nsec) / 1000 / 1000;
392
393 return currentTimeMs;
394}
395
396static constexpr std::string_view getHostState(const PowerState state)
397{
398 switch (state)
399 {
400 case PowerState::on:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700401 case PowerState::gracefulTransitionToOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700402 case PowerState::gracefulTransitionToCycleOff:
403 return "xyz.openbmc_project.State.Host.HostState.Running";
404 break;
405 case PowerState::waitForPSPowerOK:
406 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700407 case PowerState::off:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700408 case PowerState::transitionToOff:
409 case PowerState::transitionToCycleOff:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700410 case PowerState::cycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700411 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700412 return "xyz.openbmc_project.State.Host.HostState.Off";
413 break;
414 default:
415 return "";
416 break;
417 }
418};
419static constexpr std::string_view getChassisState(const PowerState state)
420{
421 switch (state)
422 {
423 case PowerState::on:
424 case PowerState::transitionToOff:
425 case PowerState::gracefulTransitionToOff:
426 case PowerState::transitionToCycleOff:
427 case PowerState::gracefulTransitionToCycleOff:
Jason M. Billse9a9e2d2019-08-08 14:01:19 -0700428 case PowerState::checkForWarmReset:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700429 return "xyz.openbmc_project.State.Chassis.PowerState.On";
430 break;
431 case PowerState::waitForPSPowerOK:
432 case PowerState::waitForSIOPowerGood:
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700433 case PowerState::off:
434 case PowerState::cycleOff:
435 return "xyz.openbmc_project.State.Chassis.PowerState.Off";
436 break;
437 default:
438 return "";
439 break;
440 }
441};
442static void savePowerState(const PowerState state)
443{
444 powerStateSaveTimer.expires_after(
445 std::chrono::milliseconds(powerOffSaveTimeMs));
446 powerStateSaveTimer.async_wait([state](const boost::system::error_code ec) {
447 if (ec)
448 {
449 // operation_aborted is expected if timer is canceled before
450 // completion.
451 if (ec != boost::asio::error::operation_aborted)
452 {
453 std::cerr << "Power-state save async_wait failed: "
454 << ec.message() << "\n";
455 }
456 return;
457 }
458 std::ofstream powerStateStream(powerControlDir / powerStateFile);
459 powerStateStream << getChassisState(state);
460 });
461}
462static void setPowerState(const PowerState state)
463{
464 powerState = state;
465 logStateTransition(state);
466
467 hostIface->set_property("CurrentHostState",
468 std::string(getHostState(powerState)));
469
470 chassisIface->set_property("CurrentPowerState",
471 std::string(getChassisState(powerState)));
472 chassisIface->set_property("LastStateChangeTime", getCurrentTimeMs());
473
474 // Save the power state for the restore policy
475 savePowerState(state);
476}
477
478enum class RestartCause
479{
480 command,
481 resetButton,
482 powerButton,
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700483 watchdog,
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700484 powerPolicyOn,
485 powerPolicyRestore,
486 softReset,
487};
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700488static boost::container::flat_set<RestartCause> causeSet;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700489static std::string getRestartCause(RestartCause cause)
490{
491 switch (cause)
492 {
493 case RestartCause::command:
494 return "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand";
495 break;
496 case RestartCause::resetButton:
497 return "xyz.openbmc_project.State.Host.RestartCause.ResetButton";
498 break;
499 case RestartCause::powerButton:
500 return "xyz.openbmc_project.State.Host.RestartCause.PowerButton";
501 break;
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700502 case RestartCause::watchdog:
503 return "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer";
504 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700505 case RestartCause::powerPolicyOn:
506 return "xyz.openbmc_project.State.Host.RestartCause."
507 "PowerPolicyAlwaysOn";
508 break;
509 case RestartCause::powerPolicyRestore:
510 return "xyz.openbmc_project.State.Host.RestartCause."
511 "PowerPolicyPreviousState";
512 break;
513 case RestartCause::softReset:
514 return "xyz.openbmc_project.State.Host.RestartCause.SoftReset";
515 break;
516 default:
517 return "xyz.openbmc_project.State.Host.RestartCause.Unknown";
518 break;
519 }
520}
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700521static void addRestartCause(const RestartCause cause)
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700522{
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700523 // Add this to the set of causes for this restart
524 causeSet.insert(cause);
525}
526static void clearRestartCause()
527{
528 // Clear the set for the next restart
529 causeSet.clear();
530}
531static void setRestartCauseProperty(const std::string& cause)
532{
533 std::cerr << "RestartCause set to " << cause << "\n";
534 restartCauseIface->set_property("RestartCause", cause);
535}
Rashmi RV89f61312020-01-22 15:41:50 +0530536
537static void resetACBootProperty()
538{
539 if ((causeSet.contains(RestartCause::command)) ||
540 (causeSet.contains(RestartCause::softReset)))
541 {
542 conn->async_method_call(
543 [](boost::system::error_code ec) {
544 if (ec)
545 {
546 std::cerr << "failed to reset ACBoot property\n";
547 }
548 },
549 "xyz.openbmc_project.Settings",
550 "/xyz/openbmc_project/control/host0/ac_boot",
551 "org.freedesktop.DBus.Properties", "Set",
552 "xyz.openbmc_project.Common.ACBoot", "ACBoot",
553 std::variant<std::string>{"False"});
554 }
555}
556
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700557static void setRestartCause()
558{
559 // Determine the actual restart cause based on the set of causes
560 std::string restartCause =
561 "xyz.openbmc_project.State.Host.RestartCause.Unknown";
562 if (causeSet.contains(RestartCause::watchdog))
563 {
564 restartCause = getRestartCause(RestartCause::watchdog);
565 }
566 else if (causeSet.contains(RestartCause::command))
567 {
568 restartCause = getRestartCause(RestartCause::command);
569 }
570 else if (causeSet.contains(RestartCause::resetButton))
571 {
572 restartCause = getRestartCause(RestartCause::resetButton);
573 }
574 else if (causeSet.contains(RestartCause::powerButton))
575 {
576 restartCause = getRestartCause(RestartCause::powerButton);
577 }
578 else if (causeSet.contains(RestartCause::powerPolicyOn))
579 {
580 restartCause = getRestartCause(RestartCause::powerPolicyOn);
581 }
582 else if (causeSet.contains(RestartCause::powerPolicyRestore))
583 {
584 restartCause = getRestartCause(RestartCause::powerPolicyRestore);
585 }
586 else if (causeSet.contains(RestartCause::softReset))
587 {
588 restartCause = getRestartCause(RestartCause::softReset);
589 }
590
591 setRestartCauseProperty(restartCause);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700592}
593
Jason M. Bills6c2ad362019-08-01 16:53:35 -0700594static void systemPowerGoodFailedLog()
595{
596 sd_journal_send(
597 "MESSAGE=PowerControl: system power good failed to assert (VR failure)",
598 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
599 "OpenBMC.0.1.SystemPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
600 sioPowerGoodWatchdogTimeMs, NULL);
601}
602
603static void psPowerOKFailedLog()
604{
605 sd_journal_send(
606 "MESSAGE=PowerControl: power supply power good failed to assert",
607 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
608 "OpenBMC.0.1.PowerSupplyPowerGoodFailed", "REDFISH_MESSAGE_ARGS=%d",
609 psPowerOKWatchdogTimeMs, NULL);
610}
611
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700612static void powerRestorePolicyLog()
613{
614 sd_journal_send("MESSAGE=PowerControl: power restore policy applied",
615 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
616 "OpenBMC.0.1.PowerRestorePolicyApplied", NULL);
617}
618
619static void powerButtonPressLog()
620{
621 sd_journal_send("MESSAGE=PowerControl: power button pressed", "PRIORITY=%i",
622 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
623 "OpenBMC.0.1.PowerButtonPressed", NULL);
624}
625
626static void resetButtonPressLog()
627{
628 sd_journal_send("MESSAGE=PowerControl: reset button pressed", "PRIORITY=%i",
629 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
630 "OpenBMC.0.1.ResetButtonPressed", NULL);
631}
632
633static void nmiButtonPressLog()
634{
635 sd_journal_send("MESSAGE=PowerControl: NMI button pressed", "PRIORITY=%i",
636 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
637 "OpenBMC.0.1.NMIButtonPressed", NULL);
638}
639
640static void nmiDiagIntLog()
641{
642 sd_journal_send("MESSAGE=PowerControl: NMI Diagnostic Interrupt",
643 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
644 "OpenBMC.0.1.NMIDiagnosticInterrupt", NULL);
645}
646
647static int initializePowerStateStorage()
648{
649 // create the power control directory if it doesn't exist
650 std::error_code ec;
651 if (!(std::filesystem::create_directories(powerControlDir, ec)))
652 {
653 if (ec.value() != 0)
654 {
655 std::cerr << "failed to create " << powerControlDir << ": "
656 << ec.message() << "\n";
657 return -1;
658 }
659 }
660 // Create the power state file if it doesn't exist
661 if (!std::filesystem::exists(powerControlDir / powerStateFile))
662 {
663 std::ofstream powerStateStream(powerControlDir / powerStateFile);
664 powerStateStream << getChassisState(powerState);
665 }
666 return 0;
667}
668
669static bool wasPowerDropped()
670{
671 std::ifstream powerStateStream(powerControlDir / powerStateFile);
672 if (!powerStateStream.is_open())
673 {
674 std::cerr << "Failed to open power state file\n";
675 return false;
676 }
677
678 std::string state;
679 std::getline(powerStateStream, state);
680 return state == "xyz.openbmc_project.State.Chassis.PowerState.On";
681}
682
683static void invokePowerRestorePolicy(const std::string& policy)
684{
685 // Async events may call this twice, but we only want to run once
686 static bool policyInvoked = false;
687 if (policyInvoked)
688 {
689 return;
690 }
691 policyInvoked = true;
692
693 std::cerr << "Power restore delay expired, invoking " << policy << "\n";
694 if (policy ==
695 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn")
696 {
697 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700698 setRestartCauseProperty(getRestartCause(RestartCause::powerPolicyOn));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700699 }
700 else if (policy == "xyz.openbmc_project.Control.Power.RestorePolicy."
701 "Policy.Restore")
702 {
703 if (wasPowerDropped())
704 {
705 std::cerr << "Power was dropped, restoring Host On state\n";
706 sendPowerControlEvent(Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -0700707 setRestartCauseProperty(
708 getRestartCause(RestartCause::powerPolicyRestore));
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700709 }
710 else
711 {
712 std::cerr << "No power drop, restoring Host Off state\n";
713 }
714 }
Jason M. Bills94ce8eb2019-09-30 10:13:25 -0700715 // We're done with the previous power state for the restore policy, so store
716 // the current state
717 savePowerState(powerState);
Ed Tanousf61ca6f2019-08-15 15:09:05 -0700718}
719
720static void powerRestorePolicyDelay(int delay)
721{
722 // Async events may call this twice, but we only want to run once
723 static bool delayStarted = false;
724 if (delayStarted)
725 {
726 return;
727 }
728 delayStarted = true;
729 // Calculate the delay from now to meet the requested delay
730 // Subtract the approximate uboot time
731 static constexpr const int ubootSeconds = 20;
732 delay -= ubootSeconds;
733 // Subtract the time since boot
734 struct sysinfo info = {};
735 if (sysinfo(&info) == 0)
736 {
737 delay -= info.uptime;
738 }
739 // 0 is the minimum delay
740 delay = std::max(delay, 0);
741
742 static boost::asio::steady_timer powerRestorePolicyTimer(io);
743 powerRestorePolicyTimer.expires_after(std::chrono::seconds(delay));
744 std::cerr << "Power restore delay of " << delay << " seconds started\n";
745 powerRestorePolicyTimer.async_wait([](const boost::system::error_code ec) {
746 if (ec)
747 {
748 // operation_aborted is expected if timer is canceled before
749 // completion.
750 if (ec != boost::asio::error::operation_aborted)
751 {
752 std::cerr << "power restore policy async_wait failed: "
753 << ec.message() << "\n";
754 }
755 return;
756 }
757 // Get Power Restore Policy
758 // In case PowerRestorePolicy is not available, set a match for it
759 static std::unique_ptr<sdbusplus::bus::match::match>
760 powerRestorePolicyMatch = std::make_unique<
761 sdbusplus::bus::match::match>(
762 *conn,
763 "type='signal',interface='org.freedesktop.DBus.Properties',"
764 "member='PropertiesChanged',arg0namespace='xyz.openbmc_"
765 "project.Control.Power.RestorePolicy'",
766 [](sdbusplus::message::message& msg) {
767 std::string interfaceName;
768 boost::container::flat_map<std::string,
769 std::variant<std::string>>
770 propertiesChanged;
771 std::string policy;
772 try
773 {
774 msg.read(interfaceName, propertiesChanged);
775 policy = std::get<std::string>(
776 propertiesChanged.begin()->second);
777 }
778 catch (std::exception& e)
779 {
780 std::cerr
781 << "Unable to read power restore policy value\n";
782 powerRestorePolicyMatch.reset();
783 return;
784 }
785 invokePowerRestorePolicy(policy);
786 powerRestorePolicyMatch.reset();
787 });
788
789 // Check if it's already on DBus
790 conn->async_method_call(
791 [](boost::system::error_code ec,
792 const std::variant<std::string>& policyProperty) {
793 if (ec)
794 {
795 return;
796 }
797 powerRestorePolicyMatch.reset();
798 const std::string* policy =
799 std::get_if<std::string>(&policyProperty);
800 if (policy == nullptr)
801 {
802 std::cerr << "Unable to read power restore policy value\n";
803 return;
804 }
805 invokePowerRestorePolicy(*policy);
806 },
807 "xyz.openbmc_project.Settings",
808 "/xyz/openbmc_project/control/host0/power_restore_policy",
809 "org.freedesktop.DBus.Properties", "Get",
810 "xyz.openbmc_project.Control.Power.RestorePolicy",
811 "PowerRestorePolicy");
812 });
813}
814
815static void powerRestorePolicyStart()
816{
817 std::cerr << "Power restore policy started\n";
818 powerRestorePolicyLog();
819
820 // Get the desired delay time
821 // In case PowerRestoreDelay is not available, set a match for it
822 static std::unique_ptr<sdbusplus::bus::match::match>
823 powerRestoreDelayMatch = std::make_unique<sdbusplus::bus::match::match>(
824 *conn,
825 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
826 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Control."
827 "Power.RestoreDelay'",
828 [](sdbusplus::message::message& msg) {
829 std::string interfaceName;
830 boost::container::flat_map<std::string, std::variant<uint16_t>>
831 propertiesChanged;
832 int delay = 0;
833 try
834 {
835 msg.read(interfaceName, propertiesChanged);
836 delay =
837 std::get<uint16_t>(propertiesChanged.begin()->second);
838 }
839 catch (std::exception& e)
840 {
841 std::cerr << "Unable to read power restore delay value\n";
842 powerRestoreDelayMatch.reset();
843 return;
844 }
845 powerRestorePolicyDelay(delay);
846 powerRestoreDelayMatch.reset();
847 });
848
849 // Check if it's already on DBus
850 conn->async_method_call(
851 [](boost::system::error_code ec,
852 const std::variant<uint16_t>& delayProperty) {
853 if (ec)
854 {
855 return;
856 }
857 powerRestoreDelayMatch.reset();
858 const uint16_t* delay = std::get_if<uint16_t>(&delayProperty);
859 if (delay == nullptr)
860 {
861 std::cerr << "Unable to read power restore delay value\n";
862 return;
863 }
864 powerRestorePolicyDelay(*delay);
865 },
866 "xyz.openbmc_project.Settings",
867 "/xyz/openbmc_project/control/power_restore_delay",
868 "org.freedesktop.DBus.Properties", "Get",
869 "xyz.openbmc_project.Control.Power.RestoreDelay", "PowerRestoreDelay");
870}
871
872static void powerRestorePolicyCheck()
873{
874 // In case ACBoot is not available, set a match for it
875 static std::unique_ptr<sdbusplus::bus::match::match> acBootMatch =
876 std::make_unique<sdbusplus::bus::match::match>(
877 *conn,
878 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
879 "PropertiesChanged',arg0namespace='xyz.openbmc_project.Common."
880 "ACBoot'",
881 [](sdbusplus::message::message& msg) {
882 std::string interfaceName;
883 boost::container::flat_map<std::string,
884 std::variant<std::string>>
885 propertiesChanged;
886 std::string acBoot;
887 try
888 {
889 msg.read(interfaceName, propertiesChanged);
890 acBoot = std::get<std::string>(
891 propertiesChanged.begin()->second);
892 }
893 catch (std::exception& e)
894 {
895 std::cerr << "Unable to read AC Boot status\n";
896 acBootMatch.reset();
897 return;
898 }
899 if (acBoot == "Unknown")
900 {
901 return;
902 }
903 if (acBoot == "True")
904 {
905 // Start the Power Restore policy
906 powerRestorePolicyStart();
907 }
908 acBootMatch.reset();
909 });
910
911 // Check if it's already on DBus
912 conn->async_method_call(
913 [](boost::system::error_code ec,
914 const std::variant<std::string>& acBootProperty) {
915 if (ec)
916 {
917 return;
918 }
919 const std::string* acBoot =
920 std::get_if<std::string>(&acBootProperty);
921 if (acBoot == nullptr)
922 {
923 std::cerr << "Unable to read AC Boot status\n";
924 return;
925 }
926 if (*acBoot == "Unknown")
927 {
928 return;
929 }
930 if (*acBoot == "True")
931 {
932 // Start the Power Restore policy
933 powerRestorePolicyStart();
934 }
935 acBootMatch.reset();
936 },
937 "xyz.openbmc_project.Settings",
938 "/xyz/openbmc_project/control/host0/ac_boot",
939 "org.freedesktop.DBus.Properties", "Get",
940 "xyz.openbmc_project.Common.ACBoot", "ACBoot");
941}
942
943static bool requestGPIOEvents(
944 const std::string& name, const std::function<void()>& handler,
945 gpiod::line& gpioLine,
946 boost::asio::posix::stream_descriptor& gpioEventDescriptor)
947{
948 // Find the GPIO line
949 gpioLine = gpiod::find_line(name);
950 if (!gpioLine)
951 {
952 std::cerr << "Failed to find the " << name << " line\n";
953 return false;
954 }
955
956 try
957 {
958 gpioLine.request(
959 {"power-control", gpiod::line_request::EVENT_BOTH_EDGES});
960 }
961 catch (std::exception&)
962 {
963 std::cerr << "Failed to request events for " << name << "\n";
964 return false;
965 }
966
967 int gpioLineFd = gpioLine.event_get_fd();
968 if (gpioLineFd < 0)
969 {
970 std::cerr << "Failed to get " << name << " fd\n";
971 return false;
972 }
973
974 gpioEventDescriptor.assign(gpioLineFd);
975
976 gpioEventDescriptor.async_wait(
977 boost::asio::posix::stream_descriptor::wait_read,
978 [&name, handler](const boost::system::error_code ec) {
979 if (ec)
980 {
981 std::cerr << name << " fd handler error: " << ec.message()
982 << "\n";
983 // TODO: throw here to force power-control to restart?
984 return;
985 }
986 handler();
987 });
988 return true;
989}
990
991static bool setGPIOOutput(const std::string& name, const int value,
992 gpiod::line& gpioLine)
993{
994 // Find the GPIO line
995 gpioLine = gpiod::find_line(name);
996 if (!gpioLine)
997 {
998 std::cerr << "Failed to find the " << name << " line.\n";
999 return false;
1000 }
1001
1002 // Request GPIO output to specified value
1003 try
1004 {
1005 gpioLine.request({__FUNCTION__, gpiod::line_request::DIRECTION_OUTPUT},
1006 value);
1007 }
1008 catch (std::exception&)
1009 {
1010 std::cerr << "Failed to request " << name << " output\n";
1011 return false;
1012 }
1013
1014 std::cerr << name << " set to " << std::to_string(value) << "\n";
1015 return true;
1016}
1017
1018static int setMaskedGPIOOutputForMs(gpiod::line& maskedGPIOLine,
1019 const std::string& name, const int value,
1020 const int durationMs)
1021{
1022 // Set the masked GPIO line to the specified value
1023 maskedGPIOLine.set_value(value);
1024 std::cerr << name << " set to " << std::to_string(value) << "\n";
1025 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1026 gpioAssertTimer.async_wait(
1027 [maskedGPIOLine, value, name](const boost::system::error_code ec) {
1028 // Set the masked GPIO line back to the opposite value
1029 maskedGPIOLine.set_value(!value);
1030 std::cerr << name << " released\n";
1031 if (ec)
1032 {
1033 // operation_aborted is expected if timer is canceled before
1034 // completion.
1035 if (ec != boost::asio::error::operation_aborted)
1036 {
1037 std::cerr << name << " async_wait failed: " + ec.message()
1038 << "\n";
1039 }
1040 }
1041 });
1042 return 0;
1043}
1044
1045static int setGPIOOutputForMs(const std::string& name, const int value,
1046 const int durationMs)
1047{
1048 // If the requested GPIO is masked, use the mask line to set the output
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001049 if (powerButtonMask && name == power_control::powerOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001050 {
1051 return setMaskedGPIOOutputForMs(powerButtonMask, name, value,
1052 durationMs);
1053 }
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001054 if (resetButtonMask && name == power_control::resetOutName)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001055 {
1056 return setMaskedGPIOOutputForMs(resetButtonMask, name, value,
1057 durationMs);
1058 }
1059
1060 // No mask set, so request and set the GPIO normally
1061 gpiod::line gpioLine;
1062 if (!setGPIOOutput(name, value, gpioLine))
1063 {
1064 return -1;
1065 }
1066 gpioAssertTimer.expires_after(std::chrono::milliseconds(durationMs));
1067 gpioAssertTimer.async_wait(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001068 [gpioLine, value, name](const boost::system::error_code ec) {
1069 // Set the GPIO line back to the opposite value
1070 gpioLine.set_value(!value);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001071 std::cerr << name << " released\n";
1072 if (ec)
1073 {
1074 // operation_aborted is expected if timer is canceled before
1075 // completion.
1076 if (ec != boost::asio::error::operation_aborted)
1077 {
1078 std::cerr << name << " async_wait failed: " << ec.message()
1079 << "\n";
1080 }
1081 }
1082 });
1083 return 0;
1084}
1085
1086static void powerOn()
1087{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001088 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001089}
1090
1091static void gracefulPowerOff()
1092{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001093 setGPIOOutputForMs(power_control::powerOutName, 0, powerPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001094}
1095
1096static void forcePowerOff()
1097{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001098 if (setGPIOOutputForMs(power_control::powerOutName, 0,
1099 forceOffPulseTimeMs) < 0)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001100 {
1101 return;
1102 }
1103
1104 // If the force off timer expires, then the PCH power-button override
1105 // failed, so attempt the Unconditional Powerdown SMBus command.
1106 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1107 if (ec)
1108 {
1109 // operation_aborted is expected if timer is canceled before
1110 // completion.
1111 if (ec != boost::asio::error::operation_aborted)
1112 {
1113 std::cerr << "Force power off async_wait failed: "
1114 << ec.message() << "\n";
1115 }
1116 return;
1117 }
1118 std::cerr << "PCH Power-button override failed. Issuing Unconditional "
1119 "Powerdown SMBus command.\n";
1120 const static constexpr size_t pchDevBusAddress = 3;
1121 const static constexpr size_t pchDevSlaveAddress = 0x44;
1122 const static constexpr size_t pchCmdReg = 0;
1123 const static constexpr size_t pchPowerDownCmd = 0x02;
1124 if (i2cSet(pchDevBusAddress, pchDevSlaveAddress, pchCmdReg,
1125 pchPowerDownCmd) < 0)
1126 {
1127 std::cerr << "Unconditional Powerdown command failed! Not sure "
1128 "what to do now.\n";
1129 }
1130 });
1131}
1132
1133static void reset()
1134{
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07001135 setGPIOOutputForMs(power_control::resetOutName, 0, resetPulseTimeMs);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001136}
1137
1138static void gracefulPowerOffTimerStart()
1139{
1140 std::cerr << "Graceful power-off timer started\n";
1141 gracefulPowerOffTimer.expires_after(
1142 std::chrono::milliseconds(gracefulPowerOffTimeMs));
1143 gracefulPowerOffTimer.async_wait([](const boost::system::error_code ec) {
1144 if (ec)
1145 {
1146 // operation_aborted is expected if timer is canceled before
1147 // completion.
1148 if (ec != boost::asio::error::operation_aborted)
1149 {
1150 std::cerr << "Graceful power-off async_wait failed: "
1151 << ec.message() << "\n";
1152 }
1153 std::cerr << "Graceful power-off timer canceled\n";
1154 return;
1155 }
1156 std::cerr << "Graceful power-off timer completed\n";
1157 sendPowerControlEvent(Event::gracefulPowerOffTimerExpired);
1158 });
1159}
1160
1161static void powerCycleTimerStart()
1162{
1163 std::cerr << "Power-cycle timer started\n";
1164 powerCycleTimer.expires_after(std::chrono::milliseconds(powerCycleTimeMs));
1165 powerCycleTimer.async_wait([](const boost::system::error_code ec) {
1166 if (ec)
1167 {
1168 // operation_aborted is expected if timer is canceled before
1169 // completion.
1170 if (ec != boost::asio::error::operation_aborted)
1171 {
1172 std::cerr << "Power-cycle async_wait failed: " << ec.message()
1173 << "\n";
1174 }
1175 std::cerr << "Power-cycle timer canceled\n";
1176 return;
1177 }
1178 std::cerr << "Power-cycle timer completed\n";
1179 sendPowerControlEvent(Event::powerCycleTimerExpired);
1180 });
1181}
1182
1183static void psPowerOKWatchdogTimerStart()
1184{
1185 std::cerr << "power supply power OK watchdog timer started\n";
1186 psPowerOKWatchdogTimer.expires_after(
1187 std::chrono::milliseconds(psPowerOKWatchdogTimeMs));
1188 psPowerOKWatchdogTimer.async_wait(
1189 [](const boost::system::error_code ec) {
1190 if (ec)
1191 {
1192 // operation_aborted is expected if timer is canceled before
1193 // completion.
1194 if (ec != boost::asio::error::operation_aborted)
1195 {
1196 std::cerr
1197 << "power supply power OK watchdog async_wait failed: "
1198 << ec.message() << "\n";
1199 }
1200 std::cerr << "power supply power OK watchdog timer canceled\n";
1201 return;
1202 }
1203 std::cerr << "power supply power OK watchdog timer expired\n";
1204 sendPowerControlEvent(Event::psPowerOKWatchdogTimerExpired);
1205 });
1206}
1207
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001208static void warmResetCheckTimerStart()
1209{
1210 std::cerr << "Warm reset check timer started\n";
1211 warmResetCheckTimer.expires_after(
1212 std::chrono::milliseconds(warmResetCheckTimeMs));
1213 warmResetCheckTimer.async_wait([](const boost::system::error_code ec) {
1214 if (ec)
1215 {
1216 // operation_aborted is expected if timer is canceled before
1217 // completion.
1218 if (ec != boost::asio::error::operation_aborted)
1219 {
1220 std::cerr << "Warm reset check async_wait failed: "
1221 << ec.message() << "\n";
1222 }
1223 std::cerr << "Warm reset check timer canceled\n";
1224 return;
1225 }
1226 std::cerr << "Warm reset check timer completed\n";
1227 sendPowerControlEvent(Event::warmResetDetected);
1228 });
1229}
1230
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001231static void pohCounterTimerStart()
1232{
1233 std::cerr << "POH timer started\n";
1234 // Set the time-out as 1 hour, to align with POH command in ipmid
1235 pohCounterTimer.expires_after(std::chrono::hours(1));
1236 pohCounterTimer.async_wait([](const boost::system::error_code& ec) {
1237 if (ec)
1238 {
1239 // operation_aborted is expected if timer is canceled before
1240 // completion.
1241 if (ec != boost::asio::error::operation_aborted)
1242 {
1243 std::cerr << "POH timer async_wait failed: " << ec.message()
1244 << "\n";
1245 }
1246 std::cerr << "POH timer canceled\n";
1247 return;
1248 }
1249
1250 if (getHostState(powerState) !=
1251 "xyz.openbmc_project.State.Host.HostState.Running")
1252 {
1253 return;
1254 }
1255
1256 conn->async_method_call(
1257 [](boost::system::error_code ec,
1258 const std::variant<uint32_t>& pohCounterProperty) {
1259 if (ec)
1260 {
1261 std::cerr << "error to get poh counter\n";
1262 return;
1263 }
1264 const uint32_t* pohCounter =
1265 std::get_if<uint32_t>(&pohCounterProperty);
1266 if (pohCounter == nullptr)
1267 {
1268 std::cerr << "unable to read poh counter\n";
1269 return;
1270 }
1271
1272 conn->async_method_call(
1273 [](boost::system::error_code ec) {
1274 if (ec)
1275 {
1276 std::cerr << "failed to set poh counter\n";
1277 }
1278 },
1279 "xyz.openbmc_project.Settings",
1280 "/xyz/openbmc_project/state/chassis0",
1281 "org.freedesktop.DBus.Properties", "Set",
1282 "xyz.openbmc_project.State.PowerOnHours", "POHCounter",
1283 std::variant<uint32_t>(*pohCounter + 1));
1284 },
1285 "xyz.openbmc_project.Settings",
1286 "/xyz/openbmc_project/state/chassis0",
1287 "org.freedesktop.DBus.Properties", "Get",
1288 "xyz.openbmc_project.State.PowerOnHours", "POHCounter");
1289
1290 pohCounterTimerStart();
1291 });
1292}
1293
1294static void currentHostStateMonitor()
1295{
Yong Li8d660212019-12-27 10:18:10 +08001296 if (getHostState(powerState) ==
1297 "xyz.openbmc_project.State.Host.HostState.Running")
1298 {
1299 pohCounterTimerStart();
1300 // Clear the restart cause set for the next restart
1301 clearRestartCause();
1302 }
1303 else
1304 {
1305 pohCounterTimer.cancel();
1306 // Set the restart cause set for this restart
1307 setRestartCause();
1308 }
1309
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001310 static auto match = sdbusplus::bus::match::match(
1311 *conn,
1312 "type='signal',member='PropertiesChanged', "
1313 "interface='org.freedesktop.DBus.Properties', "
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001314 "arg0='xyz.openbmc_project.State.Host'",
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001315 [](sdbusplus::message::message& message) {
1316 std::string intfName;
1317 std::map<std::string, std::variant<std::string>> properties;
1318
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001319 try
1320 {
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001321 message.read(intfName, properties);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001322 }
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001323 catch (std::exception& e)
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001324 {
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001325 std::cerr << "Unable to read host state\n";
1326 return;
1327 }
1328 if (properties.empty())
1329 {
1330 std::cerr << "ERROR: Empty PropertiesChanged signal received\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001331 return;
1332 }
1333
Jason M. Bills6a6485a2020-07-24 14:07:07 -07001334 // We only want to check for CurrentHostState
1335 if (properties.begin()->first != "CurrentHostState")
1336 {
1337 return;
1338 }
1339 std::string* currentHostState =
1340 std::get_if<std::string>(&(properties.begin()->second));
1341 if (currentHostState == nullptr)
1342 {
1343 std::cerr << properties.begin()->first << " property invalid\n";
1344 return;
1345 }
1346
1347 if (*currentHostState ==
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001348 "xyz.openbmc_project.State.Host.HostState.Running")
1349 {
1350 pohCounterTimerStart();
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001351 // Clear the restart cause set for the next restart
1352 clearRestartCause();
Yong Li8d660212019-12-27 10:18:10 +08001353 sd_journal_send("MESSAGE=Host system DC power is on",
1354 "PRIORITY=%i", LOG_INFO,
1355 "REDFISH_MESSAGE_ID=%s",
1356 "OpenBMC.0.1.DCPowerOn", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001357 }
1358 else
1359 {
1360 pohCounterTimer.cancel();
AppaRao Puli8f5cb6a2020-01-14 02:47:29 +05301361 // POST_COMPLETE GPIO event is not working in some platforms
1362 // when power state is changed to OFF. This resulted in
1363 // 'OperatingSystemState' to stay at 'Standby', even though
1364 // system is OFF. Set 'OperatingSystemState' to 'Inactive'
1365 // if HostState is trurned to OFF.
1366 osIface->set_property("OperatingSystemState",
1367 std::string("Inactive"));
1368
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001369 // Set the restart cause set for this restart
1370 setRestartCause();
Rashmi RV89f61312020-01-22 15:41:50 +05301371 resetACBootProperty();
Yong Li8d660212019-12-27 10:18:10 +08001372 sd_journal_send("MESSAGE=Host system DC power is off",
1373 "PRIORITY=%i", LOG_INFO,
1374 "REDFISH_MESSAGE_ID=%s",
1375 "OpenBMC.0.1.DCPowerOff", NULL);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001376 }
1377 });
1378}
1379
1380static void sioPowerGoodWatchdogTimerStart()
1381{
1382 std::cerr << "SIO power good watchdog timer started\n";
1383 sioPowerGoodWatchdogTimer.expires_after(
1384 std::chrono::milliseconds(sioPowerGoodWatchdogTimeMs));
1385 sioPowerGoodWatchdogTimer.async_wait(
1386 [](const boost::system::error_code ec) {
1387 if (ec)
1388 {
1389 // operation_aborted is expected if timer is canceled before
1390 // completion.
1391 if (ec != boost::asio::error::operation_aborted)
1392 {
1393 std::cerr << "SIO power good watchdog async_wait failed: "
1394 << ec.message() << "\n";
1395 }
1396 std::cerr << "SIO power good watchdog timer canceled\n";
1397 return;
1398 }
1399 std::cerr << "SIO power good watchdog timer completed\n";
1400 sendPowerControlEvent(Event::sioPowerGoodWatchdogTimerExpired);
1401 });
1402}
1403
1404static void powerStateOn(const Event event)
1405{
1406 logEvent(__FUNCTION__, event);
1407 switch (event)
1408 {
1409 case Event::psPowerOKDeAssert:
1410 setPowerState(PowerState::off);
1411 // DC power is unexpectedly lost, beep
1412 beep(beepPowerFail);
1413 break;
1414 case Event::sioS5Assert:
1415 setPowerState(PowerState::transitionToOff);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001416 addRestartCause(RestartCause::softReset);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001417 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001418 case Event::postCompleteDeAssert:
1419 setPowerState(PowerState::checkForWarmReset);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001420 addRestartCause(RestartCause::softReset);
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001421 warmResetCheckTimerStart();
1422 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001423 case Event::powerButtonPressed:
1424 setPowerState(PowerState::gracefulTransitionToOff);
1425 gracefulPowerOffTimerStart();
1426 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001427 case Event::resetButtonPressed:
1428 setPowerState(PowerState::checkForWarmReset);
1429 warmResetCheckTimerStart();
1430 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001431 case Event::powerOffRequest:
1432 setPowerState(PowerState::transitionToOff);
1433 forcePowerOff();
1434 break;
1435 case Event::gracefulPowerOffRequest:
1436 setPowerState(PowerState::gracefulTransitionToOff);
1437 gracefulPowerOffTimerStart();
1438 gracefulPowerOff();
1439 break;
1440 case Event::powerCycleRequest:
1441 setPowerState(PowerState::transitionToCycleOff);
1442 forcePowerOff();
1443 break;
1444 case Event::gracefulPowerCycleRequest:
1445 setPowerState(PowerState::gracefulTransitionToCycleOff);
1446 gracefulPowerOffTimerStart();
1447 gracefulPowerOff();
1448 break;
1449 case Event::resetRequest:
1450 reset();
1451 break;
1452 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001453 phosphor::logging::log<phosphor::logging::level::INFO>(
1454 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001455 break;
1456 }
1457}
1458
1459static void powerStateWaitForPSPowerOK(const Event event)
1460{
1461 logEvent(__FUNCTION__, event);
1462 switch (event)
1463 {
1464 case Event::psPowerOKAssert:
1465 // Cancel any GPIO assertions held during the transition
1466 gpioAssertTimer.cancel();
1467 psPowerOKWatchdogTimer.cancel();
1468 sioPowerGoodWatchdogTimerStart();
1469 setPowerState(PowerState::waitForSIOPowerGood);
1470 break;
1471 case Event::psPowerOKWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001472 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001473 psPowerOKFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001474 break;
Vijay Khemka0eef6b62019-10-22 12:22:52 -07001475 case Event::sioPowerGoodAssert:
1476 psPowerOKWatchdogTimer.cancel();
1477 setPowerState(PowerState::on);
1478 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001479 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001480 phosphor::logging::log<phosphor::logging::level::INFO>(
1481 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001482 break;
1483 }
1484}
1485
1486static void powerStateWaitForSIOPowerGood(const Event event)
1487{
1488 logEvent(__FUNCTION__, event);
1489 switch (event)
1490 {
1491 case Event::sioPowerGoodAssert:
1492 sioPowerGoodWatchdogTimer.cancel();
1493 setPowerState(PowerState::on);
1494 break;
1495 case Event::sioPowerGoodWatchdogTimerExpired:
Jason M. Bills273d7892020-06-17 14:46:57 -07001496 setPowerState(PowerState::off);
Jason M. Bills6c2ad362019-08-01 16:53:35 -07001497 systemPowerGoodFailedLog();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001498 break;
1499 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001500 phosphor::logging::log<phosphor::logging::level::INFO>(
1501 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001502 break;
1503 }
1504}
1505
1506static void powerStateOff(const Event event)
1507{
1508 logEvent(__FUNCTION__, event);
1509 switch (event)
1510 {
1511 case Event::psPowerOKAssert:
1512 setPowerState(PowerState::waitForSIOPowerGood);
1513 break;
1514 case Event::sioS5DeAssert:
1515 setPowerState(PowerState::waitForPSPowerOK);
1516 break;
Jason M. Bills273d7892020-06-17 14:46:57 -07001517 case Event::sioPowerGoodAssert:
1518 setPowerState(PowerState::on);
1519 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001520 case Event::powerButtonPressed:
1521 psPowerOKWatchdogTimerStart();
1522 setPowerState(PowerState::waitForPSPowerOK);
1523 break;
1524 case Event::powerOnRequest:
1525 psPowerOKWatchdogTimerStart();
1526 setPowerState(PowerState::waitForPSPowerOK);
1527 powerOn();
1528 break;
1529 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001530 phosphor::logging::log<phosphor::logging::level::INFO>(
1531 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001532 break;
1533 }
1534}
1535
1536static void powerStateTransitionToOff(const Event event)
1537{
1538 logEvent(__FUNCTION__, event);
1539 switch (event)
1540 {
1541 case Event::psPowerOKDeAssert:
1542 // Cancel any GPIO assertions held during the transition
1543 gpioAssertTimer.cancel();
1544 setPowerState(PowerState::off);
1545 break;
1546 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001547 phosphor::logging::log<phosphor::logging::level::INFO>(
1548 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001549 break;
1550 }
1551}
1552
1553static void powerStateGracefulTransitionToOff(const Event event)
1554{
1555 logEvent(__FUNCTION__, event);
1556 switch (event)
1557 {
1558 case Event::psPowerOKDeAssert:
1559 gracefulPowerOffTimer.cancel();
1560 setPowerState(PowerState::off);
1561 break;
1562 case Event::gracefulPowerOffTimerExpired:
1563 setPowerState(PowerState::on);
1564 break;
1565 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001566 phosphor::logging::log<phosphor::logging::level::INFO>(
1567 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001568 break;
1569 }
1570}
1571
1572static void powerStateCycleOff(const Event event)
1573{
1574 logEvent(__FUNCTION__, event);
1575 switch (event)
1576 {
Jason M. Bills35aa6652020-04-30 16:24:55 -07001577 case Event::psPowerOKAssert:
1578 powerCycleTimer.cancel();
1579 setPowerState(PowerState::waitForSIOPowerGood);
1580 break;
1581 case Event::sioS5DeAssert:
1582 powerCycleTimer.cancel();
1583 setPowerState(PowerState::waitForPSPowerOK);
1584 break;
1585 case Event::powerButtonPressed:
1586 powerCycleTimer.cancel();
1587 psPowerOKWatchdogTimerStart();
1588 setPowerState(PowerState::waitForPSPowerOK);
1589 break;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001590 case Event::powerCycleTimerExpired:
1591 psPowerOKWatchdogTimerStart();
1592 setPowerState(PowerState::waitForPSPowerOK);
1593 powerOn();
1594 break;
1595 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001596 phosphor::logging::log<phosphor::logging::level::INFO>(
1597 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001598 break;
1599 }
1600}
1601
1602static void powerStateTransitionToCycleOff(const Event event)
1603{
1604 logEvent(__FUNCTION__, event);
1605 switch (event)
1606 {
1607 case Event::psPowerOKDeAssert:
1608 // Cancel any GPIO assertions held during the transition
1609 gpioAssertTimer.cancel();
1610 setPowerState(PowerState::cycleOff);
1611 powerCycleTimerStart();
1612 break;
1613 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001614 phosphor::logging::log<phosphor::logging::level::INFO>(
1615 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001616 break;
1617 }
1618}
1619
1620static void powerStateGracefulTransitionToCycleOff(const Event event)
1621{
1622 logEvent(__FUNCTION__, event);
1623 switch (event)
1624 {
1625 case Event::psPowerOKDeAssert:
1626 gracefulPowerOffTimer.cancel();
1627 setPowerState(PowerState::cycleOff);
1628 powerCycleTimerStart();
1629 break;
1630 case Event::gracefulPowerOffTimerExpired:
1631 setPowerState(PowerState::on);
1632 break;
1633 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001634 phosphor::logging::log<phosphor::logging::level::INFO>(
1635 "No action taken.");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001636 break;
1637 }
1638}
1639
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001640static void powerStateCheckForWarmReset(const Event event)
1641{
1642 logEvent(__FUNCTION__, event);
1643 switch (event)
1644 {
1645 case Event::sioS5Assert:
1646 warmResetCheckTimer.cancel();
1647 setPowerState(PowerState::transitionToOff);
1648 break;
1649 case Event::warmResetDetected:
1650 setPowerState(PowerState::on);
1651 break;
P.K. Lee344dae82019-11-27 16:35:05 +08001652 case Event::psPowerOKDeAssert:
1653 warmResetCheckTimer.cancel();
1654 setPowerState(PowerState::off);
1655 // DC power is unexpectedly lost, beep
1656 beep(beepPowerFail);
1657 break;
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001658 default:
Jason M. Bills95f631c2020-06-17 14:04:29 -07001659 phosphor::logging::log<phosphor::logging::level::INFO>(
1660 "No action taken.");
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001661 break;
1662 }
1663}
1664
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001665static void psPowerOKHandler()
1666{
1667 gpiod::line_event gpioLineEvent = psPowerOKLine.event_read();
1668
1669 Event powerControlEvent =
1670 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1671 ? Event::psPowerOKAssert
1672 : Event::psPowerOKDeAssert;
1673
1674 sendPowerControlEvent(powerControlEvent);
1675 psPowerOKEvent.async_wait(
1676 boost::asio::posix::stream_descriptor::wait_read,
1677 [](const boost::system::error_code ec) {
1678 if (ec)
1679 {
1680 std::cerr << "power supply power OK handler error: "
1681 << ec.message() << "\n";
1682 return;
1683 }
1684 psPowerOKHandler();
1685 });
1686}
1687
1688static void sioPowerGoodHandler()
1689{
1690 gpiod::line_event gpioLineEvent = sioPowerGoodLine.event_read();
1691
1692 Event powerControlEvent =
1693 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE
1694 ? Event::sioPowerGoodAssert
1695 : Event::sioPowerGoodDeAssert;
1696
1697 sendPowerControlEvent(powerControlEvent);
1698 sioPowerGoodEvent.async_wait(
1699 boost::asio::posix::stream_descriptor::wait_read,
1700 [](const boost::system::error_code ec) {
1701 if (ec)
1702 {
1703 std::cerr << "SIO power good handler error: " << ec.message()
1704 << "\n";
1705 return;
1706 }
1707 sioPowerGoodHandler();
1708 });
1709}
1710
1711static void sioOnControlHandler()
1712{
1713 gpiod::line_event gpioLineEvent = sioOnControlLine.event_read();
1714
1715 bool sioOnControl =
1716 gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE;
1717 std::cerr << "SIO_ONCONTROL value changed: " << sioOnControl << "\n";
1718 sioOnControlEvent.async_wait(
1719 boost::asio::posix::stream_descriptor::wait_read,
1720 [](const boost::system::error_code ec) {
1721 if (ec)
1722 {
1723 std::cerr << "SIO ONCONTROL handler error: " << ec.message()
1724 << "\n";
1725 return;
1726 }
1727 sioOnControlHandler();
1728 });
1729}
1730
1731static void sioS5Handler()
1732{
1733 gpiod::line_event gpioLineEvent = sioS5Line.event_read();
1734
1735 Event powerControlEvent =
1736 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE
1737 ? Event::sioS5Assert
1738 : Event::sioS5DeAssert;
1739
1740 sendPowerControlEvent(powerControlEvent);
1741 sioS5Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1742 [](const boost::system::error_code ec) {
1743 if (ec)
1744 {
1745 std::cerr << "SIO S5 handler error: "
1746 << ec.message() << "\n";
1747 return;
1748 }
1749 sioS5Handler();
1750 });
1751}
1752
1753static void powerButtonHandler()
1754{
1755 gpiod::line_event gpioLineEvent = powerButtonLine.event_read();
1756
1757 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1758 {
1759 powerButtonPressLog();
1760 powerButtonIface->set_property("ButtonPressed", true);
1761 if (!powerButtonMask)
1762 {
1763 sendPowerControlEvent(Event::powerButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001764 addRestartCause(RestartCause::powerButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001765 }
1766 else
1767 {
1768 std::cerr << "power button press masked\n";
1769 }
1770 }
1771 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1772 {
1773 powerButtonIface->set_property("ButtonPressed", false);
1774 }
1775 powerButtonEvent.async_wait(
1776 boost::asio::posix::stream_descriptor::wait_read,
1777 [](const boost::system::error_code ec) {
1778 if (ec)
1779 {
1780 std::cerr << "power button handler error: " << ec.message()
1781 << "\n";
1782 return;
1783 }
1784 powerButtonHandler();
1785 });
1786}
1787
1788static void resetButtonHandler()
1789{
1790 gpiod::line_event gpioLineEvent = resetButtonLine.event_read();
1791
1792 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1793 {
1794 resetButtonPressLog();
1795 resetButtonIface->set_property("ButtonPressed", true);
1796 if (!resetButtonMask)
1797 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07001798 sendPowerControlEvent(Event::resetButtonPressed);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07001799 addRestartCause(RestartCause::resetButton);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001800 }
1801 else
1802 {
1803 std::cerr << "reset button press masked\n";
1804 }
1805 }
1806 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1807 {
1808 resetButtonIface->set_property("ButtonPressed", false);
1809 }
1810 resetButtonEvent.async_wait(
1811 boost::asio::posix::stream_descriptor::wait_read,
1812 [](const boost::system::error_code ec) {
1813 if (ec)
1814 {
1815 std::cerr << "reset button handler error: " << ec.message()
1816 << "\n";
1817 return;
1818 }
1819 resetButtonHandler();
1820 });
1821}
1822
Vijay Khemka04175c22020-10-09 14:28:11 -07001823#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07001824static constexpr auto systemdBusname = "org.freedesktop.systemd1";
1825static constexpr auto systemdPath = "/org/freedesktop/systemd1";
1826static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
1827static constexpr auto systemTargetName = "chassis-system-reset.target";
1828
1829void systemReset()
1830{
1831 conn->async_method_call(
1832 [](boost::system::error_code ec) {
1833 if (ec)
1834 {
1835 phosphor::logging::log<phosphor::logging::level::ERR>(
1836 "Failed to call chassis system reset",
1837 phosphor::logging::entry("ERR=%s", ec.message().c_str()));
1838 }
1839 },
1840 systemdBusname, systemdPath, systemdInterface, "StartUnit",
1841 systemTargetName, "replace");
1842}
Vijay Khemka04175c22020-10-09 14:28:11 -07001843#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07001844
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001845static void nmiSetEnablePorperty(bool value)
1846{
1847 conn->async_method_call(
1848 [](boost::system::error_code ec) {
1849 if (ec)
1850 {
1851 std::cerr << "failed to set NMI source\n";
1852 }
1853 },
Chen Yugang303bd582019-11-01 08:45:06 +08001854 "xyz.openbmc_project.Settings",
1855 "/xyz/openbmc_project/Chassis/Control/NMISource",
1856 "org.freedesktop.DBus.Properties", "Set",
1857 "xyz.openbmc_project.Chassis.Control.NMISource", "Enabled",
1858 std::variant<bool>{value});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001859}
1860
1861static void nmiReset(void)
1862{
1863 static constexpr const uint8_t value = 1;
1864 const static constexpr int nmiOutPulseTimeMs = 200;
1865
1866 std::cerr << "NMI out action \n";
1867 nmiOutLine.set_value(value);
1868 std::cerr << nmiOutName << " set to " << std::to_string(value) << "\n";
1869 gpioAssertTimer.expires_after(std::chrono::milliseconds(nmiOutPulseTimeMs));
1870 gpioAssertTimer.async_wait([](const boost::system::error_code ec) {
1871 // restore the NMI_OUT GPIO line back to the opposite value
1872 nmiOutLine.set_value(!value);
1873 std::cerr << nmiOutName << " released\n";
1874 if (ec)
1875 {
1876 // operation_aborted is expected if timer is canceled before
1877 // completion.
1878 if (ec != boost::asio::error::operation_aborted)
1879 {
1880 std::cerr << nmiOutName << " async_wait failed: " + ec.message()
1881 << "\n";
1882 }
1883 }
1884 });
1885 // log to redfish
1886 nmiDiagIntLog();
1887 std::cerr << "NMI out action completed\n";
1888 // reset Enable Property
1889 nmiSetEnablePorperty(false);
1890}
1891
1892static void nmiSourcePropertyMonitor(void)
1893{
1894 std::cerr << " NMI Source Property Monitor \n";
1895
1896 static std::unique_ptr<sdbusplus::bus::match::match> nmiSourceMatch =
1897 std::make_unique<sdbusplus::bus::match::match>(
1898 *conn,
1899 "type='signal',interface='org.freedesktop.DBus.Properties',"
Chen Yugang303bd582019-11-01 08:45:06 +08001900 "member='PropertiesChanged',arg0namespace='xyz.openbmc_project."
1901 "Chassis.Control."
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001902 "NMISource'",
1903 [](sdbusplus::message::message& msg) {
1904 std::string interfaceName;
1905 boost::container::flat_map<std::string,
1906 std::variant<bool, std::string>>
1907 propertiesChanged;
1908 std::string state;
1909 bool value = true;
1910 try
1911 {
1912 msg.read(interfaceName, propertiesChanged);
1913 if (propertiesChanged.begin()->first == "Enabled")
1914 {
1915 value =
1916 std::get<bool>(propertiesChanged.begin()->second);
1917 std::cerr
1918 << " NMI Enabled propertiesChanged value: " << value
1919 << "\n";
1920 nmiEnabled = value;
1921 if (nmiEnabled)
1922 {
1923 nmiReset();
1924 }
1925 }
1926 }
1927 catch (std::exception& e)
1928 {
1929 std::cerr << "Unable to read NMI source\n";
1930 return;
1931 }
1932 });
1933}
1934
1935static void setNmiSource()
1936{
1937 conn->async_method_call(
1938 [](boost::system::error_code ec) {
1939 if (ec)
1940 {
1941 std::cerr << "failed to set NMI source\n";
1942 }
1943 },
Chen Yugang303bd582019-11-01 08:45:06 +08001944 "xyz.openbmc_project.Settings",
1945 "/xyz/openbmc_project/Chassis/Control/NMISource",
1946 "org.freedesktop.DBus.Properties", "Set",
1947 "xyz.openbmc_project.Chassis.Control.NMISource", "BMCSource",
1948 std::variant<std::string>{"xyz.openbmc_project.Chassis.Control."
1949 "NMISource.BMCSourceSignal.FpBtn"});
Ed Tanousf61ca6f2019-08-15 15:09:05 -07001950 // set Enable Property
1951 nmiSetEnablePorperty(true);
1952}
1953
1954static void nmiButtonHandler()
1955{
1956 gpiod::line_event gpioLineEvent = nmiButtonLine.event_read();
1957
1958 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1959 {
1960 nmiButtonPressLog();
1961 nmiButtonIface->set_property("ButtonPressed", true);
1962 if (nmiButtonMasked)
1963 {
1964 std::cerr << "NMI button press masked\n";
1965 }
1966 else
1967 {
1968 setNmiSource();
1969 }
1970 }
1971 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1972 {
1973 nmiButtonIface->set_property("ButtonPressed", false);
1974 }
1975 nmiButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
1976 [](const boost::system::error_code ec) {
1977 if (ec)
1978 {
1979 std::cerr << "NMI button handler error: "
1980 << ec.message() << "\n";
1981 return;
1982 }
1983 nmiButtonHandler();
1984 });
1985}
1986
1987static void idButtonHandler()
1988{
1989 gpiod::line_event gpioLineEvent = idButtonLine.event_read();
1990
1991 if (gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE)
1992 {
1993 idButtonIface->set_property("ButtonPressed", true);
1994 }
1995 else if (gpioLineEvent.event_type == gpiod::line_event::RISING_EDGE)
1996 {
1997 idButtonIface->set_property("ButtonPressed", false);
1998 }
1999 idButtonEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
2000 [](const boost::system::error_code& ec) {
2001 if (ec)
2002 {
2003 std::cerr << "ID button handler error: "
2004 << ec.message() << "\n";
2005 return;
2006 }
2007 idButtonHandler();
2008 });
2009}
2010
2011static void postCompleteHandler()
2012{
2013 gpiod::line_event gpioLineEvent = postCompleteLine.event_read();
2014
2015 bool postComplete =
2016 gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002017 if (postComplete)
2018 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002019 sendPowerControlEvent(Event::postCompleteAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002020 osIface->set_property("OperatingSystemState", std::string("Standby"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002021 }
2022 else
2023 {
Jason M. Billse9a9e2d2019-08-08 14:01:19 -07002024 sendPowerControlEvent(Event::postCompleteDeAssert);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002025 osIface->set_property("OperatingSystemState", std::string("Inactive"));
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002026 }
2027 postCompleteEvent.async_wait(
2028 boost::asio::posix::stream_descriptor::wait_read,
2029 [](const boost::system::error_code ec) {
2030 if (ec)
2031 {
2032 std::cerr << "POST complete handler error: " << ec.message()
2033 << "\n";
2034 return;
2035 }
2036 postCompleteHandler();
2037 });
2038}
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302039
2040static int loadConfigValues()
2041{
2042 const std::string configFilePath =
2043 "/usr/share/x86-power-control/power-config-host" + power_control::node +
2044 ".json";
2045 std::ifstream configFile(configFilePath.c_str());
2046 if (!configFile.is_open())
2047 {
2048 std::cerr << "loadConfigValues : Cannot open config path\n ";
2049 return -1;
2050 }
2051 auto data = nlohmann::json::parse(configFile, nullptr);
2052
2053 if (data.is_discarded())
2054 {
2055 std::cerr << "Power config readings JSON parser failure";
2056 return -1;
2057 }
2058
2059 if (data.contains("IdButton"))
2060 {
2061 idButtonName = data["IdButton"];
2062 }
2063
2064 if (data.contains("NMIButton"))
2065 {
2066 nmiButtonName = data["NMIButton"];
2067 }
2068
2069 if (data.contains("NMIOut"))
2070 {
2071 nmiOutName = data["NMIOut"];
2072 }
2073
2074 if (data.contains("PostComplete"))
2075 {
2076 postCompleteName = data["PostComplete"];
2077 }
2078
2079 if (data.contains("PwrButton"))
2080 {
2081 powerButtonName = data["PwrButton"];
2082 }
2083
2084 if (data.contains("PwrOK"))
2085 {
2086 powerOkName = data["PwrOK"];
2087 }
2088
2089 if (data.contains("PwrOut"))
2090 {
2091 powerOutName = data["PwrOut"];
2092 }
2093
2094 if (data.contains("RstButton"))
2095 {
2096 resetButtonName = data["RstButton"];
2097 }
2098
2099 if (data.contains("RstOut"))
2100 {
2101 resetOutName = data["RstOut"];
2102 }
2103
2104 if (data.contains("SIOOnCtl"))
2105 {
2106 sioOnControlName = data["SIOOnCtl"];
2107 }
2108
2109 if (data.contains("SIOPwrGd"))
2110 {
2111 sioPwrGoodName = data["SIOPwrGd"];
2112 }
2113
2114 if (data.contains("SIOS5"))
2115 {
2116 sioS5Name = data["SIOS5"];
2117 }
2118
2119 return 0;
2120}
2121
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002122} // namespace power_control
2123
2124int main(int argc, char* argv[])
2125{
2126 std::cerr << "Start Chassis power control service...\n";
2127 power_control::conn =
2128 std::make_shared<sdbusplus::asio::connection>(power_control::io);
2129
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302130 // Load GPIO's through json config file
2131 if (power_control::loadConfigValues() == -1)
2132 {
2133 std::cerr << "Host" << power_control::node << ": "
2134 << "Error in Parsing...\n";
2135 }
2136
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002137 // Request all the dbus names
2138 power_control::conn->request_name("xyz.openbmc_project.State.Host");
2139 power_control::conn->request_name("xyz.openbmc_project.State.Chassis");
2140 power_control::conn->request_name(
2141 "xyz.openbmc_project.State.OperatingSystem");
2142 power_control::conn->request_name("xyz.openbmc_project.Chassis.Buttons");
Chen Yugang174ec662019-08-19 19:58:49 +08002143 power_control::conn->request_name("xyz.openbmc_project.Control.Host.NMI");
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002144 power_control::conn->request_name(
2145 "xyz.openbmc_project.Control.Host.RestartCause");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002146
2147 // Request PS_PWROK GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302148 if (!power_control::powerOkName.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002149 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302150 if (!power_control::requestGPIOEvents(
2151 power_control::powerOkName, power_control::psPowerOKHandler,
2152 power_control::psPowerOKLine, power_control::psPowerOKEvent))
2153 {
2154 return -1;
2155 }
2156 }
2157 else
2158 {
2159 std::cerr
2160 << "PowerOk name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002161 return -1;
2162 }
2163
2164 // Request SIO_POWER_GOOD GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302165 if (!power_control::sioPwrGoodName.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002166 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302167 if (!power_control::requestGPIOEvents(
2168 power_control::sioPwrGoodName,
2169 power_control::sioPowerGoodHandler,
2170 power_control::sioPowerGoodLine,
2171 power_control::sioPowerGoodEvent))
2172 {
2173 return -1;
2174 }
2175 }
2176 else
2177 {
2178 std::cerr
2179 << "sioPwrGood name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002180 return -1;
2181 }
2182
2183 // Request SIO_ONCONTROL GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302184 if (!power_control::sioOnControlName.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002185 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302186 if (!power_control::requestGPIOEvents(
2187 power_control::sioOnControlName,
2188 power_control::sioOnControlHandler,
2189 power_control::sioOnControlLine,
2190 power_control::sioOnControlEvent))
2191 {
2192 return -1;
2193 }
2194 }
2195 else
2196 {
2197 std::cerr
2198 << "sioOnControl name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002199 return -1;
2200 }
2201
2202 // Request SIO_S5 GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302203 if (!power_control::sioS5Name.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002204 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302205 if (!power_control::requestGPIOEvents(
2206 power_control::sioS5Name, power_control::sioS5Handler,
2207 power_control::sioS5Line, power_control::sioS5Event))
2208 {
2209 return -1;
2210 }
2211 }
2212 else
2213 {
2214 std::cerr << "sioS5 name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002215 return -1;
2216 }
2217
2218 // Request POWER_BUTTON GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302219 if (!power_control::powerButtonName.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002220 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302221 if (!power_control::requestGPIOEvents(power_control::powerButtonName,
2222 power_control::powerButtonHandler,
2223 power_control::powerButtonLine,
2224 power_control::powerButtonEvent))
2225 {
2226 return -1;
2227 }
2228 }
2229 else
2230 {
2231 std::cerr
2232 << "powerButton name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002233 return -1;
2234 }
2235
2236 // Request RESET_BUTTON GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302237 if (!power_control::resetButtonName.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002238 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302239 if (!power_control::requestGPIOEvents(power_control::resetButtonName,
2240 power_control::resetButtonHandler,
2241 power_control::resetButtonLine,
2242 power_control::resetButtonEvent))
2243 {
2244 return -1;
2245 }
2246 }
2247 else
2248 {
2249 std::cerr
2250 << "resetButton name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002251 return -1;
2252 }
2253
2254 // Request NMI_BUTTON GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302255 if (!power_control::nmiButtonName.empty())
2256 {
2257 power_control::requestGPIOEvents(
2258 power_control::nmiButtonName, power_control::nmiButtonHandler,
2259 power_control::nmiButtonLine, power_control::nmiButtonEvent);
2260 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002261
2262 // Request ID_BUTTON GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302263 if (!power_control::idButtonName.empty())
2264 {
2265 power_control::requestGPIOEvents(
2266 power_control::idButtonName, power_control::idButtonHandler,
2267 power_control::idButtonLine, power_control::idButtonEvent);
2268 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002269
2270 // Request POST_COMPLETE GPIO events
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302271 if (!power_control::postCompleteName.empty())
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002272 {
Priyatharshan Pe4d7f2b2020-06-22 22:41:42 +05302273 if (!power_control::requestGPIOEvents(
2274 power_control::postCompleteName,
2275 power_control::postCompleteHandler,
2276 power_control::postCompleteLine,
2277 power_control::postCompleteEvent))
2278 {
2279 return -1;
2280 }
2281 }
2282 else
2283 {
2284 std::cerr
2285 << "postComplete name should be configured from json config file\n";
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002286 return -1;
2287 }
2288
2289 // initialize NMI_OUT GPIO.
Vijay Khemka33a532d2019-11-14 16:50:35 -08002290 power_control::setGPIOOutput(power_control::nmiOutName, 0,
2291 power_control::nmiOutLine);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002292
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002293 // Initialize POWER_OUT and RESET_OUT GPIO.
2294 gpiod::line line;
2295 if (!power_control::setGPIOOutput(power_control::powerOutName, 1, line))
2296 {
2297 return -1;
2298 }
2299
2300 if (!power_control::setGPIOOutput(power_control::resetOutName, 1, line))
2301 {
2302 return -1;
2303 }
2304
2305 // Release line
2306 line.reset();
2307
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002308 // Initialize the power state
2309 power_control::powerState = power_control::PowerState::off;
2310 // Check power good
2311 if (power_control::psPowerOKLine.get_value() > 0)
2312 {
2313 power_control::powerState = power_control::PowerState::on;
2314 }
2315
2316 // Initialize the power state storage
2317 if (power_control::initializePowerStateStorage() < 0)
2318 {
2319 return -1;
2320 }
2321
2322 // Check if we need to start the Power Restore policy
2323 power_control::powerRestorePolicyCheck();
2324
Vijay Khemka33a532d2019-11-14 16:50:35 -08002325 if (power_control::nmiOutLine)
2326 power_control::nmiSourcePropertyMonitor();
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002327
2328 std::cerr << "Initializing power state. ";
2329 power_control::logStateTransition(power_control::powerState);
2330
2331 // Power Control Service
2332 sdbusplus::asio::object_server hostServer =
2333 sdbusplus::asio::object_server(power_control::conn);
2334
2335 // Power Control Interface
2336 power_control::hostIface = hostServer.add_interface(
2337 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host");
2338
2339 power_control::hostIface->register_property(
2340 "RequestedHostTransition",
2341 std::string("xyz.openbmc_project.State.Host.Transition.Off"),
2342 [](const std::string& requested, std::string& resp) {
2343 if (requested == "xyz.openbmc_project.State.Host.Transition.Off")
2344 {
2345 sendPowerControlEvent(
2346 power_control::Event::gracefulPowerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002347 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002348 }
2349 else if (requested ==
2350 "xyz.openbmc_project.State.Host.Transition.On")
2351 {
2352 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002353 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002354 }
2355 else if (requested ==
2356 "xyz.openbmc_project.State.Host.Transition.Reboot")
2357 {
Jason M. Billse7520ba2020-01-31 11:19:03 -08002358 sendPowerControlEvent(power_control::Event::powerCycleRequest);
2359 addRestartCause(power_control::RestartCause::command);
2360 }
2361 else if (requested == "xyz.openbmc_project.State.Host.Transition."
2362 "GracefulWarmReboot")
2363 {
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002364 sendPowerControlEvent(
2365 power_control::Event::gracefulPowerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002366 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002367 }
Jason M. Billse7520ba2020-01-31 11:19:03 -08002368 else if (requested == "xyz.openbmc_project.State.Host.Transition."
2369 "ForceWarmReboot")
2370 {
2371 sendPowerControlEvent(power_control::Event::resetRequest);
2372 addRestartCause(power_control::RestartCause::command);
2373 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002374 else
2375 {
2376 std::cerr << "Unrecognized host state transition request.\n";
2377 throw std::invalid_argument("Unrecognized Transition Request");
2378 return 0;
2379 }
2380 resp = requested;
2381 return 1;
2382 });
2383 power_control::hostIface->register_property(
2384 "CurrentHostState",
2385 std::string(power_control::getHostState(power_control::powerState)));
2386
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002387 power_control::hostIface->initialize();
2388
2389 // Chassis Control Service
2390 sdbusplus::asio::object_server chassisServer =
2391 sdbusplus::asio::object_server(power_control::conn);
2392
2393 // Chassis Control Interface
2394 power_control::chassisIface =
2395 chassisServer.add_interface("/xyz/openbmc_project/state/chassis0",
2396 "xyz.openbmc_project.State.Chassis");
2397
2398 power_control::chassisIface->register_property(
2399 "RequestedPowerTransition",
2400 std::string("xyz.openbmc_project.State.Chassis.Transition.Off"),
2401 [](const std::string& requested, std::string& resp) {
2402 if (requested == "xyz.openbmc_project.State.Chassis.Transition.Off")
2403 {
2404 sendPowerControlEvent(power_control::Event::powerOffRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002405 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002406 }
2407 else if (requested ==
2408 "xyz.openbmc_project.State.Chassis.Transition.On")
2409 {
2410 sendPowerControlEvent(power_control::Event::powerOnRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002411 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002412 }
2413 else if (requested ==
2414 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2415 {
2416 sendPowerControlEvent(power_control::Event::powerCycleRequest);
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002417 addRestartCause(power_control::RestartCause::command);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002418 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002419 else
2420 {
2421 std::cerr << "Unrecognized chassis state transition request.\n";
2422 throw std::invalid_argument("Unrecognized Transition Request");
2423 return 0;
2424 }
2425 resp = requested;
2426 return 1;
2427 });
2428 power_control::chassisIface->register_property(
2429 "CurrentPowerState",
2430 std::string(power_control::getChassisState(power_control::powerState)));
2431 power_control::chassisIface->register_property(
2432 "LastStateChangeTime", power_control::getCurrentTimeMs());
2433
2434 power_control::chassisIface->initialize();
2435
Vijay Khemka04175c22020-10-09 14:28:11 -07002436#ifdef CHASSIS_SYSTEM_RESET
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002437 // Chassis System Service
2438 sdbusplus::asio::object_server chassisSysServer =
2439 sdbusplus::asio::object_server(power_control::conn);
2440
2441 // Chassis System Interface
2442 power_control::chassisSysIface = chassisSysServer.add_interface(
2443 "/xyz/openbmc_project/state/chassis_system0",
2444 "xyz.openbmc_project.State.Chassis");
2445
2446 power_control::chassisSysIface->register_property(
2447 "RequestedPowerTransition",
2448 std::string("xyz.openbmc_project.State.Chassis.Transition.On"),
2449 [](const std::string& requested, std::string& resp) {
2450 if (requested ==
2451 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle")
2452 {
2453 power_control::systemReset();
2454 addRestartCause(power_control::RestartCause::command);
2455 }
2456 else
2457 {
2458 std::cerr << "Unrecognized chassis system state transition "
2459 "request.\n";
2460 throw std::invalid_argument("Unrecognized Transition Request");
2461 return 0;
2462 }
2463 resp = requested;
2464 return 1;
2465 });
2466 power_control::chassisSysIface->register_property(
2467 "CurrentPowerState",
2468 std::string(power_control::getChassisState(power_control::powerState)));
2469 power_control::chassisSysIface->register_property(
2470 "LastStateChangeTime", power_control::getCurrentTimeMs());
2471
2472 power_control::chassisSysIface->initialize();
Vijay Khemka04175c22020-10-09 14:28:11 -07002473#endif
Vijay Khemka75ad0cf2020-04-02 15:23:51 -07002474
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002475 // Buttons Service
2476 sdbusplus::asio::object_server buttonsServer =
2477 sdbusplus::asio::object_server(power_control::conn);
2478
2479 // Power Button Interface
2480 power_control::powerButtonIface = buttonsServer.add_interface(
2481 "/xyz/openbmc_project/chassis/buttons/power",
2482 "xyz.openbmc_project.Chassis.Buttons");
2483
2484 power_control::powerButtonIface->register_property(
2485 "ButtonMasked", false, [](const bool requested, bool& current) {
2486 if (requested)
2487 {
2488 if (power_control::powerButtonMask)
2489 {
2490 return 1;
2491 }
2492 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002493 power_control::powerOutName, 1,
2494 power_control::powerButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002495 {
2496 throw std::runtime_error("Failed to request GPIO");
2497 return 0;
2498 }
2499 std::cerr << "Power Button Masked.\n";
2500 }
2501 else
2502 {
2503 if (!power_control::powerButtonMask)
2504 {
2505 return 1;
2506 }
2507 std::cerr << "Power Button Un-masked\n";
2508 power_control::powerButtonMask.reset();
2509 }
2510 // Update the mask setting
2511 current = requested;
2512 return 1;
2513 });
2514
2515 // Check power button state
2516 bool powerButtonPressed = power_control::powerButtonLine.get_value() == 0;
2517 power_control::powerButtonIface->register_property("ButtonPressed",
2518 powerButtonPressed);
2519
2520 power_control::powerButtonIface->initialize();
2521
2522 // Reset Button Interface
2523 power_control::resetButtonIface = buttonsServer.add_interface(
2524 "/xyz/openbmc_project/chassis/buttons/reset",
2525 "xyz.openbmc_project.Chassis.Buttons");
2526
2527 power_control::resetButtonIface->register_property(
2528 "ButtonMasked", false, [](const bool requested, bool& current) {
2529 if (requested)
2530 {
2531 if (power_control::resetButtonMask)
2532 {
2533 return 1;
2534 }
2535 if (!power_control::setGPIOOutput(
Vijay Khemka0dc7d4c2019-10-22 12:30:17 -07002536 power_control::resetOutName, 1,
2537 power_control::resetButtonMask))
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002538 {
2539 throw std::runtime_error("Failed to request GPIO");
2540 return 0;
2541 }
2542 std::cerr << "Reset Button Masked.\n";
2543 }
2544 else
2545 {
2546 if (!power_control::resetButtonMask)
2547 {
2548 return 1;
2549 }
2550 std::cerr << "Reset Button Un-masked\n";
2551 power_control::resetButtonMask.reset();
2552 }
2553 // Update the mask setting
2554 current = requested;
2555 return 1;
2556 });
2557
2558 // Check reset button state
2559 bool resetButtonPressed = power_control::resetButtonLine.get_value() == 0;
2560 power_control::resetButtonIface->register_property("ButtonPressed",
2561 resetButtonPressed);
2562
2563 power_control::resetButtonIface->initialize();
2564
Vijay Khemka33a532d2019-11-14 16:50:35 -08002565 if (power_control::nmiButtonLine)
2566 {
2567 // NMI Button Interface
2568 power_control::nmiButtonIface = buttonsServer.add_interface(
2569 "/xyz/openbmc_project/chassis/buttons/nmi",
2570 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002571
Vijay Khemka33a532d2019-11-14 16:50:35 -08002572 power_control::nmiButtonIface->register_property(
2573 "ButtonMasked", false, [](const bool requested, bool& current) {
2574 if (power_control::nmiButtonMasked == requested)
2575 {
2576 // NMI button mask is already set as requested, so no change
2577 return 1;
2578 }
2579 if (requested)
2580 {
2581 std::cerr << "NMI Button Masked.\n";
2582 power_control::nmiButtonMasked = true;
2583 }
2584 else
2585 {
2586 std::cerr << "NMI Button Un-masked.\n";
2587 power_control::nmiButtonMasked = false;
2588 }
2589 // Update the mask setting
2590 current = power_control::nmiButtonMasked;
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002591 return 1;
Vijay Khemka33a532d2019-11-14 16:50:35 -08002592 });
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002593
Vijay Khemka33a532d2019-11-14 16:50:35 -08002594 // Check NMI button state
2595 bool nmiButtonPressed = power_control::nmiButtonLine.get_value() == 0;
2596 power_control::nmiButtonIface->register_property("ButtonPressed",
2597 nmiButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002598
Vijay Khemka33a532d2019-11-14 16:50:35 -08002599 power_control::nmiButtonIface->initialize();
2600 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002601
Vijay Khemka33a532d2019-11-14 16:50:35 -08002602 if (power_control::nmiOutLine)
2603 {
2604 // NMI out Service
2605 sdbusplus::asio::object_server nmiOutServer =
2606 sdbusplus::asio::object_server(power_control::conn);
Chen Yugang174ec662019-08-19 19:58:49 +08002607
Vijay Khemka33a532d2019-11-14 16:50:35 -08002608 // NMI out Interface
2609 power_control::nmiOutIface =
2610 nmiOutServer.add_interface("/xyz/openbmc_project/control/host0/nmi",
2611 "xyz.openbmc_project.Control.Host.NMI");
2612 power_control::nmiOutIface->register_method("NMI",
2613 power_control::nmiReset);
2614 power_control::nmiOutIface->initialize();
2615 }
Chen Yugang174ec662019-08-19 19:58:49 +08002616
Vijay Khemka33a532d2019-11-14 16:50:35 -08002617 if (power_control::idButtonLine)
2618 {
2619 // ID Button Interface
2620 power_control::idButtonIface = buttonsServer.add_interface(
2621 "/xyz/openbmc_project/chassis/buttons/id",
2622 "xyz.openbmc_project.Chassis.Buttons");
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002623
Vijay Khemka33a532d2019-11-14 16:50:35 -08002624 // Check ID button state
2625 bool idButtonPressed = power_control::idButtonLine.get_value() == 0;
2626 power_control::idButtonIface->register_property("ButtonPressed",
2627 idButtonPressed);
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002628
Vijay Khemka33a532d2019-11-14 16:50:35 -08002629 power_control::idButtonIface->initialize();
2630 }
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002631
2632 // OS State Service
2633 sdbusplus::asio::object_server osServer =
2634 sdbusplus::asio::object_server(power_control::conn);
2635
2636 // OS State Interface
2637 power_control::osIface = osServer.add_interface(
2638 "/xyz/openbmc_project/state/os",
2639 "xyz.openbmc_project.State.OperatingSystem.Status");
2640
2641 // Get the initial OS state based on POST complete
2642 // 0: Asserted, OS state is "Standby" (ready to boot)
2643 // 1: De-Asserted, OS state is "Inactive"
2644 std::string osState = power_control::postCompleteLine.get_value() > 0
2645 ? "Inactive"
2646 : "Standby";
2647
2648 power_control::osIface->register_property("OperatingSystemState",
2649 std::string(osState));
2650
2651 power_control::osIface->initialize();
2652
Jason M. Bills7d4aaac2019-09-19 14:03:44 -07002653 // Restart Cause Service
2654 sdbusplus::asio::object_server restartCauseServer =
2655 sdbusplus::asio::object_server(power_control::conn);
2656
2657 // Restart Cause Interface
2658 power_control::restartCauseIface = restartCauseServer.add_interface(
2659 "/xyz/openbmc_project/control/host0/restart_cause",
2660 "xyz.openbmc_project.Control.Host.RestartCause");
2661
2662 power_control::restartCauseIface->register_property(
2663 "RestartCause",
2664 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"));
2665
2666 power_control::restartCauseIface->register_property(
2667 "RequestedRestartCause",
2668 std::string("xyz.openbmc_project.State.Host.RestartCause.Unknown"),
2669 [](const std::string& requested, std::string& resp) {
2670 if (requested ==
2671 "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
2672 {
2673 power_control::addRestartCause(
2674 power_control::RestartCause::watchdog);
2675 }
2676 else
2677 {
2678 throw std::invalid_argument(
2679 "Unrecognized RestartCause Request");
2680 return 0;
2681 }
2682
2683 std::cerr << "RestartCause requested: " << requested << "\n";
2684 resp = requested;
2685 return 1;
2686 });
2687
2688 power_control::restartCauseIface->initialize();
2689
Yong Li8d660212019-12-27 10:18:10 +08002690 power_control::currentHostStateMonitor();
2691
Ed Tanousf61ca6f2019-08-15 15:09:05 -07002692 power_control::io.run();
2693
2694 return 0;
2695}