summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/watchdog')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb33
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format98
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt52
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json12
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp264
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch348
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service16
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend8
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb12
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf3
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service11
11 files changed, 857 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb
new file mode 100644
index 000000000..706e08646
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog.bb
@@ -0,0 +1,33 @@
+
+SUMMARY = "FRB2 timer service"
+DESCRIPTION = "The FRB2 timer service will monitor the mailbox register 0\
+and start a watchdog for FRB2 if the data is 1(BIOS will write this value)"
+
+SRC_URI = "\
+ file://CMakeLists.txt \
+ file://frb2-watchdog.cpp \
+ "
+PV = "0.1"
+
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658"
+
+S = "${WORKDIR}"
+
+inherit cmake
+inherit pkgconfig
+
+DEPENDS += " \
+ systemd \
+ sdbusplus \
+ phosphor-logging \
+ phosphor-dbus-interfaces \
+ boost \
+ "
+
+RDEPENDS:${PN} += " \
+ libsystemd \
+ sdbusplus \
+ phosphor-logging \
+ phosphor-dbus-interfaces \
+ "
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format
new file mode 100644
index 000000000..dd2770837
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/.clang-format
@@ -0,0 +1,98 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^[<"](gtest|gmock)'
+ Priority: 5
+ - Regex: '^"config.h"'
+ Priority: -1
+ - Regex: '^".*\.hpp"'
+ Priority: 1
+ - Regex: '^<.*\.h>'
+ Priority: 2
+ - Regex: '^<.*'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 4
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt
new file mode 100644
index 000000000..bd5567d31
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/CMakeLists.txt
@@ -0,0 +1,52 @@
+cmake_minimum_required (VERSION 3.5 FATAL_ERROR)
+project (frb2-watchdog CXX)
+set (CMAKE_CXX_STANDARD 17)
+set (CMAKE_CXX_STANDARD_REQUIRED ON)
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti")
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
+
+# boost support
+find_package (Boost REQUIRED)
+# pkg_check_modules(Boost boost REQUIRED)
+include_directories (${Boost_INCLUDE_DIRS})
+add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY)
+add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED)
+add_definitions (-DBOOST_ALL_NO_LIB)
+add_definitions (-DBOOST_NO_RTTI)
+add_definitions (-DBOOST_NO_TYPEID)
+add_definitions (-DBOOST_ASIO_DISABLE_THREADS)
+
+# import libsystemd
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (SYSTEMD libsystemd REQUIRED)
+include_directories (${SYSTEMD_INCLUDE_DIRS})
+link_directories (${SYSTEMD_LIBRARY_DIRS})
+
+# import sdbusplus
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (SDBUSPLUSPLUS sdbusplus REQUIRED)
+include_directories (${SDBUSPLUSPLUS_INCLUDE_DIRS})
+link_directories (${SDBUSPLUSPLUS_LIBRARY_DIRS})
+
+# import phosphor-logging
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (LOGGING phosphor-logging REQUIRED)
+include_directories (${LOGGING_INCLUDE_DIRS})
+link_directories (${LOGGING_LIBRARY_DIRS})
+
+# import phosphor-dbus-interfaces
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (DBUSINTERFACE phosphor-dbus-interfaces REQUIRED)
+include_directories (${DBUSINTERFACE_INCLUDE_DIRS})
+link_directories (${DBUSINTERFACE_LIBRARY_DIRS})
+
+add_executable (frb2-watchdog frb2-watchdog.cpp)
+
+target_link_libraries (${PROJECT_NAME} systemd)
+target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES})
+target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES})
+target_link_libraries (${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES}
+ phosphor_logging)
+install (TARGETS frb2-watchdog DESTINATION bin)
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json
new file mode 100644
index 000000000..583c255a3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/cmake-format.json
@@ -0,0 +1,12 @@
+{
+ "enum_char": ".",
+ "line_ending": "unix",
+ "bullet_char": "*",
+ "max_subargs_per_line": 99,
+ "command_case": "lower",
+ "tab_size": 4,
+ "line_width": 80,
+ "separate_fn_name_with_space": true,
+ "dangle_parens": true,
+ "separate_ctrl_name_with_space": true
+} \ No newline at end of file
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp
new file mode 100644
index 000000000..bae54f335
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/frb2-watchdog/frb2-watchdog.cpp
@@ -0,0 +1,264 @@
+/* Copyright 2018 Intel
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <boost/asio/buffers_iterator.hpp>
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/posix/stream_descriptor.hpp>
+#include <boost/asio/read_until.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/container/flat_set.hpp>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/timer.hpp>
+#include <vector>
+#include <xyz/openbmc_project/State/Watchdog/server.hpp>
+
+void handleResponse(const boost::system::error_code &err,
+ std::size_t bytes_transferred);
+
+static int mailboxDevFd = -1;
+
+static boost::asio::io_service io;
+static auto conn = std::make_shared<sdbusplus::asio::connection>(io);
+boost::asio::ip::tcp::socket mailBoxDevSocket(io);
+boost::asio::deadline_timer pollTimer(io);
+boost::asio::posix::stream_descriptor inputDevice(io);
+
+// mailbox registre data[0:0] for FRB2 enable bit
+boost::asio::streambuf readBuf(1);
+std::string dataRead;
+
+// FRB2 watchdog timeout is 6 minutes
+static constexpr unsigned int frb2TimerIntervalMs = 360 * 1000;
+
+// mailbox device polling time interval is 2 seconds
+static constexpr unsigned int pollMs = 2000;
+
+static constexpr unsigned int frb2Started = 1;
+static constexpr unsigned int frb2Stopped = 0;
+
+// FRB2 status
+static uint8_t frb2Status = frb2Stopped;
+
+static constexpr const char *mailboxDevName = "/dev/aspeed-mbox";
+
+static constexpr const char frb2Bus[] = "xyz.openbmc_project.FRB2";
+static constexpr const char frb2Obj[] = "/xyz/openbmc_project/FRB2";
+static constexpr const char frb2Intf[] = "xyz.openbmc_project.FRB2";
+
+static constexpr char powerBus[] = "xyz.openbmc_project.Chassis.Control.Power";
+static constexpr char powerPath[] =
+ "/xyz/openbmc_project/Chassis/Control/Power0";
+static constexpr char powerIntf[] = "xyz.openbmc_project.Chassis.Control.Power";
+
+static constexpr char wdBus[] = "xyz.openbmc_project.Watchdog";
+static constexpr char wdPath[] = "/xyz/openbmc_project/watchdog/host0";
+static constexpr char wdIntf[] = "xyz.openbmc_project.State.Watchdog";
+static constexpr char propIntf[] = "org.freedesktop.DBus.Properties";
+
+typedef boost::asio::buffers_iterator<boost::asio::const_buffers_1> iterator;
+
+// check if FRB2 bit is 0x1
+std::pair<iterator, bool> matchFRB2(iterator begin, iterator end)
+{
+ unsigned char ch = 0;
+ iterator i = begin;
+
+ while (i != end)
+ {
+ ch = static_cast<unsigned char>(*i);
+ if (ch & 0x1)
+ {
+ return std::make_pair(i, true);
+ }
+ i++;
+ }
+
+ return std::make_pair(i, false);
+}
+
+static void startRead()
+{
+ boost::asio::async_read_until(inputDevice, readBuf, matchFRB2,
+ [&](const boost::system::error_code &ec,
+ std::size_t bytes_transferred) {
+ handleResponse(ec, bytes_transferred);
+ });
+}
+
+template <typename T> void setProperty(const std::string &key, const T &val)
+{
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "setProperty", phosphor::logging::entry("KEY=%s", key.c_str()));
+
+ try
+ {
+ conn->async_method_call(
+ [](const boost::system::error_code &err) {
+ if (err)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "async_method_call error!",
+ phosphor::logging::entry(
+ "ERROR=%s",
+ boost::system::system_error(err).what()));
+ }
+ },
+ wdBus, wdPath, propIntf, "Set", wdIntf, key, std::variant<T>(val));
+ }
+ catch (sdbusplus::exception::SdBusError &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Dbus error!", phosphor::logging::entry("ERROR=%s", e.what()));
+ }
+}
+void handleResponse(const boost::system::error_code &err,
+ std::size_t bytes_transferred)
+{
+ std::istream responseStream(&readBuf);
+ std::string response;
+ int n = 0;
+ uint64_t interval = frb2TimerIntervalMs;
+
+ std::getline(responseStream, response);
+ responseStream.clear();
+
+ if (err == boost::system::errc::bad_file_descriptor)
+ {
+
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "bad file descriptor");
+ return; // we're being destroyed
+ }
+
+ if (!err)
+ {
+ // FRB2 is set by BIOS
+ if (frb2Stopped == frb2Status)
+ {
+ // start FRB2 watchdog
+ frb2Status = frb2Started;
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "FRB2 enable, start FRB2 watchdog");
+ setProperty(
+ "ExpireAction",
+ std::string(
+ "xyz.openbmc_project.State.Watchdog.Action.HardReset"));
+ setProperty("Interval", interval);
+ setProperty("TimeRemaining", interval);
+ setProperty("Initialized", true);
+ setProperty("Enabled", true);
+ }
+ }
+ else if (err == boost::asio::error::misc_errors::not_found)
+ {
+ // FRB2 is clear, stop FRB2 watchdog if it is started
+ if (frb2Started == frb2Status)
+ {
+ frb2Status = frb2Stopped;
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "FRB2 is unset, stop FRB2 watchdog");
+ setProperty("Enabled", false);
+ }
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "handleResponse error!",
+ phosphor::logging::entry("ERROR=%s",
+ boost::system::system_error(err).what()));
+ }
+
+ pollTimer.expires_from_now(boost::posix_time::milliseconds(pollMs));
+ pollTimer.async_wait(
+ [](const boost::system::error_code &ec) { startRead(); });
+}
+
+int main(int argc, char **argv)
+{
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "Monitor FRB2 signal");
+
+ sdbusplus::bus::match_t biosPostSignal(
+ static_cast<sdbusplus::bus::bus &>(*conn),
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::member("PostCompleted") +
+ sdbusplus::bus::match::rules::path(powerPath) +
+ sdbusplus::bus::match::rules::interface(powerIntf),
+ [](sdbusplus::message::message &msg) {
+ uint8_t value = 0;
+ ssize_t rc = 0;
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "BIOS post completed signal");
+ // stop FRB2 and clean mailbox
+ value = 0;
+ rc = ::pwrite(mailboxDevFd, &value, 1, 0);
+ if (rc != 1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "mailbox write error!");
+ }
+ setProperty("Enabled", false);
+ frb2Status = frb2Stopped;
+ return;
+ });
+
+ conn->request_name(frb2Bus);
+
+ auto server = sdbusplus::asio::object_server(conn);
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> frb2Iface =
+ server.add_interface(frb2Obj, frb2Intf);
+
+ frb2Iface->register_property("frb2Status", frb2Status);
+
+ frb2Iface->initialize();
+
+ mailboxDevFd = ::open(mailboxDevName, O_RDWR | O_CLOEXEC);
+ if (mailboxDevFd < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "mailbox device open fail!");
+ return -1;
+ }
+
+ inputDevice.assign(mailboxDevFd);
+
+ startRead();
+
+ io.run();
+
+ ::close(mailboxDevFd);
+
+ return 0;
+}
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch
new file mode 100644
index 000000000..48bc4a086
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch
@@ -0,0 +1,348 @@
+From a7d1d3770a650df8fe61d594885fcb388ac2ca42 Mon Sep 17 00:00:00 2001
+From: James Feist <james.feist@linux.intel.com>
+Date: Mon, 17 Jun 2019 12:00:58 -0700
+Subject: [PATCH] Customize phosphor-watchdog for Intel platforms
+
+This patch adds various changes to phosphor-watchdog that are
+required for compatibility with Intel platforms.
+
+ 1. Add Redfish messages for watchdog timeout and pre-interrupt
+ 2. Use dbus properties for power control insted of service files
+ 3. Use host status to enable/disable watchdog
+ 4. Set preTimeoutInterruptOccurFlag
+ 5. Assign watchdog cause for correct reset cause reporting
+ 6. Add NMI Pre-Interrupt support for IPMI watchdog timer.
+
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+Signed-off-by: Ren Yu <yux.ren@intel.com>
+Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
+Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
+Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
+Signed-off-by: Sunita Kumari <sunitax.kumari@intel.com>
+
+%% original patch: 0001-Customize-phosphor-watchdog-for-Intel-platforms.patch
+---
+ src/watchdog.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++++---
+ src/watchdog.hpp | 22 ++++-
+ 2 files changed, 240 insertions(+), 11 deletions(-)
+
+diff --git a/src/watchdog.cpp b/src/watchdog.cpp
+index 7de98ae3e70f..f96faebc7368 100644
+--- a/src/watchdog.cpp
++++ b/src/watchdog.cpp
+@@ -5,8 +5,10 @@
+ #include <phosphor-logging/elog.hpp>
+ #include <phosphor-logging/log.hpp>
+ #include <sdbusplus/exception.hpp>
++#include <systemd/sd-journal.h>
+ #include <string_view>
+ #include <xyz/openbmc_project/Common/error.hpp>
++#include <xyz/openbmc_project/State/Host/server.hpp>
+
+ namespace phosphor
+ {
+@@ -19,10 +21,86 @@ using namespace phosphor::logging;
+ using sdbusplus::exception::SdBusError;
+ using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+-// systemd service to kick start a target.
+-constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
+-constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
+-constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
++const static constexpr char* currentHostState = "CurrentHostState";
++const static constexpr char* hostStatusOff =
++ "xyz.openbmc_project.State.Host.HostState.Off";
++
++const static constexpr char* actionDescription = " due to Watchdog timeout";
++const static constexpr char* hardResetDescription = "Hard Reset - System reset";
++const static constexpr char* powerOffDescription =
++ "Power Down - System power down";
++const static constexpr char* powerCycleDescription =
++ "Power Cycle - System power cycle";
++const static constexpr char* timerExpiredDescription = "Timer expired";
++
++const static constexpr char* preInterruptActionNone =
++ "xyz.openbmc_project.State.Watchdog.PreTimeoutInterruptAction.None";
++
++const static constexpr char* preInterruptDescriptionSMI = "SMI";
++const static constexpr char* preInterruptDescriptionNMI = "NMI";
++const static constexpr char* preInterruptDescriptionMI = "Messaging Interrupt";
++
++const static constexpr char* reservedDescription = "Reserved";
++
++const static constexpr char* timerUseDescriptionBIOSFRB2 = "BIOS FRB2";
++const static constexpr char* timerUseDescriptionBIOSPOST = "BIOS/POST";
++const static constexpr char* timerUseDescriptionOSLoad = "OSLoad";
++const static constexpr char* timerUseDescriptionSMSOS = "SMS/OS";
++const static constexpr char* timerUseDescriptionOEM = "OEM";
++
++namespace restart
++{
++static constexpr const char* busName =
++ "xyz.openbmc_project.Control.Host.RestartCause";
++static constexpr const char* path =
++ "/xyz/openbmc_project/control/host0/restart_cause";
++static constexpr const char* interface =
++ "xyz.openbmc_project.Control.Host.RestartCause";
++static constexpr const char* property = "RequestedRestartCause";
++} // namespace restart
++
++// chassis state manager service
++namespace chassis
++{
++static constexpr const char* busName = "xyz.openbmc_project.State.Chassis";
++static constexpr const char* path = "/xyz/openbmc_project/state/chassis0";
++static constexpr const char* interface = "xyz.openbmc_project.State.Chassis";
++static constexpr const char* request = "RequestedPowerTransition";
++} // namespace chassis
++
++namespace host
++{
++static constexpr const char* busName = "xyz.openbmc_project.State.Host";
++static constexpr const char* path = "/xyz/openbmc_project/state/host0";
++static constexpr const char* interface = "xyz.openbmc_project.State.Host";
++static constexpr const char* request = "RequestedHostTransition";
++} // namespace host
++
++namespace nmi
++{
++static constexpr const char* busName = "xyz.openbmc_project.Control.Host.NMI";
++static constexpr const char* path = "/xyz/openbmc_project/control/host0/nmi";
++static constexpr const char* interface = "xyz.openbmc_project.Control.Host.NMI";
++static constexpr const char* request = "NMI";
++
++} // namespace nmi
++
++void Watchdog::powerStateChangedHandler(
++ const std::map<std::string, std::variant<std::string>>& props)
++{
++ const auto iter = props.find(currentHostState);
++ if (iter != props.end())
++ {
++ const std::string* powerState = std::get_if<std::string>(&iter->second);
++ if (powerState && (*powerState == hostStatusOff))
++ {
++ if (timerEnabled())
++ {
++ enabled(false);
++ }
++ }
++ }
++}
+
+ void Watchdog::resetTimeRemaining(bool enableWatchdog)
+ {
+@@ -108,13 +186,111 @@ uint64_t Watchdog::interval(uint64_t value)
+ // Optional callback function on timer expiration
+ void Watchdog::timeOutHandler()
+ {
++ PreTimeoutInterruptAction preTimeoutInterruptAction = preTimeoutInterrupt();
++ std::string preInterruptActionMessageArgs{};
++
+ Action action = expireAction();
++ std::string actionMessageArgs{};
++
++ expiredTimerUse(currentTimerUse());
++
++ TimerUse timeUser = expiredTimerUse();
++ std::string timeUserMessage{};
++
+ if (!this->enabled())
+ {
+ action = fallback->action;
+ }
+
+- expiredTimerUse(currentTimerUse());
++ switch (timeUser)
++ {
++ case Watchdog::TimerUse::BIOSFRB2:
++ timeUserMessage = timerUseDescriptionBIOSFRB2;
++ break;
++ case Watchdog::TimerUse::BIOSPOST:
++ timeUserMessage = timerUseDescriptionBIOSPOST;
++ break;
++ case Watchdog::TimerUse::OSLoad:
++ timeUserMessage = timerUseDescriptionOSLoad;
++ break;
++ case Watchdog::TimerUse::SMSOS:
++ timeUserMessage = timerUseDescriptionSMSOS;
++ break;
++ case Watchdog::TimerUse::OEM:
++ timeUserMessage = timerUseDescriptionOEM;
++ break;
++ default:
++ timeUserMessage = reservedDescription;
++ break;
++ }
++
++ switch (action)
++ {
++ case Watchdog::Action::HardReset:
++ actionMessageArgs = std::string(hardResetDescription) +
++ std::string(actionDescription);
++ break;
++ case Watchdog::Action::PowerOff:
++ actionMessageArgs = std::string(powerOffDescription) +
++ std::string(actionDescription);
++ break;
++ case Watchdog::Action::PowerCycle:
++ actionMessageArgs = std::string(powerCycleDescription) +
++ std::string(actionDescription);
++ break;
++ case Watchdog::Action::None:
++ actionMessageArgs = timerExpiredDescription;
++ break;
++ default:
++ actionMessageArgs = reservedDescription;
++ break;
++ }
++
++ // Log into redfish event log
++ sd_journal_send("MESSAGE=IPMIWatchdog: Timed out ACTION=%s",
++ convertForMessage(action).c_str(), "PRIORITY=%i", LOG_INFO,
++ "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.IPMIWatchdog",
++ "REDFISH_MESSAGE_ARGS=%s. timer use: %s",
++ actionMessageArgs.c_str(), timeUserMessage.c_str(), NULL);
++
++ switch (preTimeoutInterruptAction)
++ {
++ case Watchdog::PreTimeoutInterruptAction::SMI:
++ preInterruptActionMessageArgs = preInterruptDescriptionSMI;
++ break;
++ case Watchdog::PreTimeoutInterruptAction::NMI:
++ preInterruptActionMessageArgs = preInterruptDescriptionNMI;
++ break;
++ case Watchdog::PreTimeoutInterruptAction::MI:
++ preInterruptActionMessageArgs = preInterruptDescriptionMI;
++ break;
++ default:
++ preInterruptActionMessageArgs = reservedDescription;
++ break;
++ }
++
++ if (preInterruptActionNone != convertForMessage(preTimeoutInterruptAction))
++ {
++ preTimeoutInterruptOccurFlag(true);
++
++ sd_journal_send("MESSAGE=IPMIWatchdog: Pre Timed out Interrupt=%s",
++ convertForMessage(preTimeoutInterruptAction).c_str(),
++ "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.IPMIWatchdog",
++ "REDFISH_MESSAGE_ARGS=Timer interrupt - %s due to "
++ "Watchdog timeout. timer use: %s",
++ preInterruptActionMessageArgs.c_str(),
++ timeUserMessage.c_str(), NULL);
++
++ if (preTimeoutInterruptAction ==
++ Watchdog::PreTimeoutInterruptAction::NMI)
++ {
++ sdbusplus::message::message preTimeoutInterruptHandler;
++ preTimeoutInterruptHandler = bus.new_method_call(
++ nmi::busName, nmi::path, nmi::interface, nmi::request);
++ bus.call_noreply(preTimeoutInterruptHandler);
++ }
++ }
+
+ auto target = actionTargetMap.find(action);
+ if (target == actionTargetMap.end())
+@@ -147,12 +323,45 @@ void Watchdog::timeOutHandler()
+
+ try
+ {
+- auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
+- SYSTEMD_INTERFACE, "StartUnit");
+- method.append(target->second);
+- method.append("replace");
++ sdbusplus::message::message method;
++ if (action == Watchdog::Action::HardReset)
++ {
++ auto method = bus.new_method_call(
++ restart::busName, restart::path,
++ "org.freedesktop.DBus.Properties", "Set");
++ method.append(
++ restart::interface, restart::property,
++ std::variant<std::string>("xyz.openbmc_project.State.Host."
++ "RestartCause.WatchdogTimer"));
++ bus.call_noreply(method);
+
+- bus.call_noreply(method);
++ method = bus.new_method_call(host::busName, host::path,
++ "org.freedesktop.DBus.Properties",
++ "Set");
++ method.append(host::interface, host::request,
++ std::variant<std::string>(target->second));
++ bus.call_noreply(method);
++ }
++ else
++ {
++ if (action == Watchdog::Action::PowerCycle)
++ {
++ auto method = bus.new_method_call(
++ restart::busName, restart::path,
++ "org.freedesktop.DBus.Properties", "Set");
++ method.append(restart::interface, restart::property,
++ std::variant<std::string>(
++ "xyz.openbmc_project.State.Host."
++ "RestartCause.WatchdogTimer"));
++ bus.call_noreply(method);
++ }
++ method = bus.new_method_call(chassis::busName, chassis::path,
++ "org.freedesktop.DBus.Properties",
++ "Set");
++ method.append(chassis::interface, chassis::request,
++ std::variant<std::string>(target->second));
++ bus.call_noreply(method);
++ }
+ }
+ catch (const SdBusError& e)
+ {
+diff --git a/src/watchdog.hpp b/src/watchdog.hpp
+index 736e71f68fcc..79158f192f40 100644
+--- a/src/watchdog.hpp
++++ b/src/watchdog.hpp
+@@ -73,7 +73,17 @@ class Watchdog : public WatchdogInherits
+ bus(bus), actionTargetMap(std::move(actionTargetMap)),
+ fallback(std::move(fallback)), minInterval(minInterval),
+ timer(event, std::bind(&Watchdog::timeOutHandler, this)),
+- objPath(objPath)
++ objPath(objPath), powerStateChangedSignal(
++ bus,
++ sdbusplus::bus::match::rules::propertiesChanged(
++ "/xyz/openbmc_project/state/host0",
++ "xyz.openbmc_project.State.Host"),
++ [this](sdbusplus::message::message& msg) {
++ std::string objectName;
++ std::map<std::string, std::variant<std::string>> props;
++ msg.read(objectName, props);
++ powerStateChangedHandler(props);
++ })
+ {
+ // Use default if passed in otherwise just use default that comes
+ // with object
+@@ -90,6 +100,12 @@ class Watchdog : public WatchdogInherits
+ tryFallbackOrDisable();
+ }
+
++ /** @brief Disable watchdog when power status change meet
++ * the specific requirement
++ */
++ void powerStateChangedHandler(
++ const std::map<std::string, std::variant<std::string>>& props);
++
+ /** @brief Resets the TimeRemaining to the configured Interval
+ * Optionally enables the watchdog.
+ *
+@@ -178,6 +194,10 @@ class Watchdog : public WatchdogInherits
+ /** @brief Contained timer object */
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
+
++ /** @brief Optional Callback handler when power status change meet
++ * the specific requirement */
++ sdbusplus::bus::match_t powerStateChangedSignal;
++
+ /** @brief Optional Callback handler on timer expirartion */
+ void timeOutHandler();
+
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service
new file mode 100644
index 000000000..007e39d8a
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Phosphor Watchdog
+
+[Service]
+ExecStart=/usr/bin/env phosphor-watchdog --continue --service=xyz.openbmc_project.Watchdog \
+ --path=/xyz/openbmc_project/watchdog/host0 \
+ --action_target=xyz.openbmc_project.State.Watchdog.Action.HardReset=xyz.openbmc_project.State.Host.Transition.ForceWarmReboot \
+ --action_target=xyz.openbmc_project.State.Watchdog.Action.PowerOff=xyz.openbmc_project.State.Chassis.Transition.Off \
+ --action_target=xyz.openbmc_project.State.Watchdog.Action.PowerCycle=xyz.openbmc_project.State.Chassis.Transition.PowerCycle
+
+SyslogIdentifier=phosphor-watchdog
+BusName =xyz.openbmc_project.Watchdog
+Type=dbus
+
+[Install]
+WantedBy=basic.target
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend
new file mode 100644
index 000000000..ad2984fa9
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend
@@ -0,0 +1,8 @@
+FILESEXTRAPATHS:append := ":${THISDIR}/${PN}"
+
+SRC_URI += "file://0001-Customize-phosphor-watchdog-for-Intel-platforms.patch \
+ "
+
+# Remove the override to keep service running after DC cycle
+SYSTEMD_OVERRIDE_${PN}:remove = "poweron.conf:phosphor-watchdog@poweron.service.d/poweron.conf"
+SYSTEMD_SERVICE:${PN} = "phosphor-watchdog.service"
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb
new file mode 100644
index 000000000..b5724710e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog.bb
@@ -0,0 +1,12 @@
+SUMMARY = "System watchdog"
+DESCRIPTION = "BMC hardware watchdog service that is used to reset BMC \
+ when unrecoverable events occurs"
+
+inherit allarch
+inherit obmc-phosphor-systemd
+
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658"
+
+SYSTEMD_SERVICE:${PN} += "system-watchdog.service"
+SYSTEMD_ENVIRONMENT_FILE_${PN} += "obmc/system-watchdog/system-watchdog.conf"
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf
new file mode 100644
index 000000000..defe830a1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/obmc/system-watchdog/system-watchdog.conf
@@ -0,0 +1,3 @@
+TIMEOUT=60
+INTERVAL=10
+DEVICE=/dev/watchdog1
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service
new file mode 100644
index 000000000..1564fda20
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog/system-watchdog/system-watchdog.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=BMC Hardware Watchdog Daemon
+
+[Service]
+EnvironmentFile=/etc/default/obmc/system-watchdog/system-watchdog.conf
+ExecStart=/sbin/watchdog -T ${{TIMEOUT}} -t ${{INTERVAL}} -F ${{DEVICE}}
+KillSignal=SIGKILL
+
+[Install]
+WantedBy=basic.target
+