meta-quanta: gbs: add ipmi watchdog

the upstream watchdog lose some command parameters and implementation
we reference Intel-BMC repo[*1] apply patches for normal execute with
x86-power-control

[*1]: https://github.com/Intel-BMC/openbmc/tree/intel/meta-openbmc-mods/meta-common/recipes-phosphor/watchdog

Signed-off-by: George Hung <george.hung@quantatw.com>
Change-Id: I8965a81a77073503bc042ae8ffa5c5733a9778dd
diff --git a/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces/0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch b/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces/0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch
new file mode 100644
index 0000000..67fa590
--- /dev/null
+++ b/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces/0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch
@@ -0,0 +1,57 @@
+From 6e9a19c43acac7d4254910906329d98d7b59085a Mon Sep 17 00:00:00 2001
+From: Ren Yu <yux.ren@intel.com>
+Date: Fri, 24 May 2019 14:55:10 +0800
+Subject: [PATCH] Add the pre-timeout interrupt defined in IPMI spec
+
+The IPMI watchdog pre-timeout interrupt is used to set the different
+pre-timeout interrupt source. Add them as a dbus property,
+IPMI set/get watchdog commands will use it.
+
+Signed-off-by: Ren Yu <yux.ren@intel.com>
+---
+ xyz/openbmc_project/State/Watchdog.interface.yaml | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml
+index 2fc47d8..6dfa9b9 100644
+--- a/xyz/openbmc_project/State/Watchdog.interface.yaml
++++ b/xyz/openbmc_project/State/Watchdog.interface.yaml
+@@ -33,6 +33,11 @@ properties:
+       description: >
+           The action the watchdog should perform when it expires.
+       default: 'HardReset'
++    - name: PreTimeoutInterrupt
++      type: enum[self.PreTimeoutInterruptAction]
++      description: >
++          The BMC generates the selected interrupt before the timer expires.
++      default: 'None'
+     - name: Interval
+       type: uint64
+       description: >
+@@ -73,6 +78,23 @@ enumerations:
+          description: >
+            Perform a power cycle of the system.
+ 
++   - name: PreTimeoutInterruptAction
++     description: >
++       The type of PreTimeout Interrupt.
++     values:
++       - name: 'None'
++         description: >
++           Do nothing.
++       - name: 'SMI'
++         description: >
++           SMI.
++       - name: 'NMI'
++         description: >
++           NMI / Diagnostic Interrupt.
++       - name: 'MI'
++         description: >
++           Messaging Interrupt.
++
+    - name: TimerUse
+      description: >
+        The type of timer use.
+-- 
+2.7.4
+
diff --git a/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces/0025-Add-PreInterruptFlag-properity-in-DBUS.patch b/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces/0025-Add-PreInterruptFlag-properity-in-DBUS.patch
new file mode 100644
index 0000000..d7e66ab
--- /dev/null
+++ b/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces/0025-Add-PreInterruptFlag-properity-in-DBUS.patch
@@ -0,0 +1,39 @@
+From b7c487750c05dcc081219ccdd4ef539beef6aa30 Mon Sep 17 00:00:00 2001
+From: Ren Yu <yux.ren@intel.com>
+Date: Mon, 29 Jul 2019 10:51:12 +0800
+Subject: [PATCH] Add PreInterruptFlag properity in DBUS.
+
+PreTimeoutInterruptOccurFlag in DBUS would be set 'true'
+when watchdog pre-timeout interrupt occurred.
+
+Tested:
+Enable command(raw 0x06 0x31) that get message flag
+can set right bit about watchdog,
+need record PreTimeoutInterruptOccurFlag
+at xyz.openbmmc_project.State.Watchdog when watchdog
+pre-timeout interrupt occurred.
+
+Signed-off-by: Ren Yu <yux.ren@intel.com>
+---
+ xyz/openbmc_project/State/Watchdog.interface.yaml | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/xyz/openbmc_project/State/Watchdog.interface.yaml b/xyz/openbmc_project/State/Watchdog.interface.yaml
+index bf4cca0..6579368 100644
+--- a/xyz/openbmc_project/State/Watchdog.interface.yaml
++++ b/xyz/openbmc_project/State/Watchdog.interface.yaml
+@@ -59,6 +59,11 @@ properties:
+       description: >
+           The timer user at the time of expiration.
+       default: 'Reserved'
++    - name: PreTimeoutInterruptOccurFlag
++      type: boolean
++      description: >
++          PreTimeoutInterruptOccurFlag that preTimeoutInterrupt action occurred.
++      default: false
+ 
+ enumerations:
+    - name: Action
+-- 
+2.7.4
+
diff --git a/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend b/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend
new file mode 100644
index 0000000..0b5bff9
--- /dev/null
+++ b/meta-gbs/recipes-phosphor/dbus/phosphor-dbus-interfaces_%.bbappend
@@ -0,0 +1,5 @@
+FILESEXTRAPATHS_prepend_gbs := "${THISDIR}/${PN}:"
+
+SRC_URI_append_gbs = " file://0024-Add-the-pre-timeout-interrupt-defined-in-IPMI-spec.patch \
+                       file://0025-Add-PreInterruptFlag-properity-in-DBUS.patch \
+                     "
diff --git a/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch b/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch
new file mode 100644
index 0000000..22670d3
--- /dev/null
+++ b/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog/0001-Customize-phosphor-watchdog-for-Intel-platforms.patch
@@ -0,0 +1,313 @@
+From ac0c216ac2c273c620579fd1308c8c225e0cca36 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
+
+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>
+---
+ watchdog.cpp | 193 ++++++++++++++++++++++++++++++++++++++++++++++++---
+ watchdog.hpp |  23 +++++-
+ 2 files changed, 206 insertions(+), 10 deletions(-)
+
+diff --git a/watchdog.cpp b/watchdog.cpp
+index 57e9050..3b5356f 100644
+--- a/watchdog.cpp
++++ b/watchdog.cpp
+@@ -1,11 +1,14 @@
+ #include "watchdog.hpp"
+ 
++#include <systemd/sd-journal.h>
++
+ #include <algorithm>
+ #include <chrono>
+ #include <phosphor-logging/elog.hpp>
+ #include <phosphor-logging/log.hpp>
+ #include <sdbusplus/exception.hpp>
+ #include <xyz/openbmc_project/Common/error.hpp>
++#include <xyz/openbmc_project/State/Host/server.hpp>
+ 
+ namespace phosphor
+ {
+@@ -18,10 +21,69 @@ 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
++
++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)
+ {
+@@ -107,13 +169,102 @@ 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);
++    }
+ 
+     auto target = actionTargetMap.find(action);
+     if (target == actionTargetMap.end())
+@@ -133,10 +284,11 @@ void Watchdog::timeOutHandler()
+ 
+         try
+         {
+-            auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
+-                                              SYSTEMD_INTERFACE, "StartUnit");
+-            method.append(target->second);
+-            method.append("replace");
++            auto 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);
+         }
+@@ -147,6 +299,29 @@ void Watchdog::timeOutHandler()
+                             entry("ERROR=%s", e.what()));
+             commit<InternalFailure>();
+         }
++
++        // set restart cause for watchdog HardReset & PowerCycle actions
++        if ((action == Watchdog::Action::HardReset) ||
++            (action == Watchdog::Action::PowerCycle))
++        {
++            try
++            {
++                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(method);
++            }
++            catch (sdbusplus::exception_t& e)
++            {
++                log<level::ERR>("Failed to set HostRestartCause property",
++                                entry("ERROR=%s", e.what()));
++                commit<InternalFailure>();
++            }
++        }
+     }
+ 
+     tryFallbackOrDisable();
+diff --git a/watchdog.hpp b/watchdog.hpp
+index 7de9bb3..b004b7a 100644
+--- a/watchdog.hpp
++++ b/watchdog.hpp
+@@ -68,7 +68,18 @@ class Watchdog : public WatchdogInherits
+         WatchdogInherits(bus, objPath),
+         bus(bus), actionTargetMap(std::move(actionTargetMap)),
+         fallback(std::move(fallback)), minInterval(minInterval),
+-        timer(event, std::bind(&Watchdog::timeOutHandler, this))
++        timer(event, std::bind(&Watchdog::timeOutHandler, this)),
++        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);
++            })
+     {
+         // We set the watchdog interval with the default value.
+         interval(interval());
+@@ -77,6 +88,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.
+      *
+@@ -165,6 +182,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();
+ 
diff --git a/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service b/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog/phosphor-watchdog.service
new file mode 100644
index 0000000..5ef1a41
--- /dev/null
+++ b/meta-gbs/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.Chassis.Transition.Reset \
+         --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-gbs/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend b/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend
new file mode 100644
index 0000000..9c491ec
--- /dev/null
+++ b/meta-gbs/recipes-phosphor/watchdog/phosphor-watchdog_%.bbappend
@@ -0,0 +1,7 @@
+FILESEXTRAPATHS_prepend_gbs := "${THISDIR}/${PN}:"
+
+SRC_URI_append_gbs = " file://0001-Customize-phosphor-watchdog-for-Intel-platforms.patch"
+
+# Remove the override to keep service running after DC cycle
+SYSTEMD_OVERRIDE_${PN}_remove_gbs = "poweron.conf:phosphor-watchdog@poweron.service.d/poweron.conf"
+SYSTEMD_SERVICE_${PN}_gbs = "phosphor-watchdog.service"