Hi Nhi,

On Fri, Oct 22, 2021 at 13:17:41 +0700, Nhi Pham wrote:
> The FailSafeDxe is a driver for the FailSafe feature which reverts the
> system's configuration to known good values if the system fails to boot
> up multiple times. Also, this driver implements the Watchdog Timer
> Architectural Protocol to reset the system if it hangs, which is
> implemented by the MdeModulePkg/Universal/WatchdogTimerDxe module. So,
> the WDT is now used exclusively by the FailSafeDxe.

I guess I skimmed this message previously. Taking a closer look, I am
very confused by this design (and the commit message), on several
levels.

1) Why do you need a custom watchdog driver?
2) Why is it integrated into FailSafeDxe instead of being a standalone
   driver?
3) Given that it's integrated, why do you install it as an
   implementation of EFI_WATCHDOG_TIMER_ARCH_PROTOCOL?
4) Given that it's installed, how can it be exclusively for the use of
   FailSafeDxe?

> By default, when system starts, it configures the secure watchdog timer
> with a default value of 5 minutes. If the system boots up cleanly to the
> considered good stage, the counter is cleared as it indicates FailSafe
> monitor (ATF) that has booted up successfully. If the timer expires, it
> is considered a failed boot and the system is rebooted.

How?
(more below)

> Cc: Thang Nguyen <[email protected]>
> Cc: Chuong Tran <[email protected]>
> Cc: Phong Vo <[email protected]>
> Cc: Leif Lindholm <[email protected]>
> Cc: Michael D Kinney <[email protected]>
> Cc: Ard Biesheuvel <[email protected]>
> Cc: Nate DeSimone <[email protected]>
> 
> Signed-off-by: Nhi Pham <[email protected]>
> Reviewed-by: Leif Lindholm <[email protected]>
> ---
>  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc              |   6 +-
>  Platform/Ampere/JadePkg/Jade.fdf                                  |   6 +-
>  Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.inf |  51 +++
>  Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafe.h      |  44 +++
>  Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.h      |  29 ++
>  Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.c   | 243 
> +++++++++++++
>  Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.c      | 357 
> ++++++++++++++++++++
>  7 files changed, 734 insertions(+), 2 deletions(-)
> 
> diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc 
> b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
> index 69a6caa56752..bfe66f332c56 100644
> --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
> +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc
> @@ -588,7 +588,11 @@ [Components.common]
>    # Timer
>    #
>    ArmPkg/Drivers/TimerDxe/TimerDxe.inf
> -  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
> +
> +  #
> +  # FailSafe and Watchdog Timer
> +  #
> +  Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.inf
>  
>    #
>    # ARM GIC Dxe
> diff --git a/Platform/Ampere/JadePkg/Jade.fdf 
> b/Platform/Ampere/JadePkg/Jade.fdf
> index 6e228d4ecb89..49e38db1bce4 100644
> --- a/Platform/Ampere/JadePkg/Jade.fdf
> +++ b/Platform/Ampere/JadePkg/Jade.fdf
> @@ -185,7 +185,11 @@ [FV.FvMain]
>    # Timer
>    #
>    INF ArmPkg/Drivers/TimerDxe/TimerDxe.inf
> -  INF MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
> +
> +  #
> +  # FailSafe and Watchdog Timer
> +  #
> +  INF Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.inf
>  
>    #
>    # ARM GIC Dxe
> diff --git 
> a/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.inf 
> b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.inf
> new file mode 100644
> index 000000000000..cea69516d0bb
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.inf
> @@ -0,0 +1,51 @@
> +## @file
> +#
> +# Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x0001001B
> +  BASE_NAME                      = FailSafeDxe
> +  FILE_GUID                      = 7BC4F970-B1CF-11E6-80F5-76304DEC7EB7
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = FailSafeDxeEntryPoint
> +
> +[Sources]
> +  FailSafe.h
> +  FailSafeDxe.c
> +  Watchdog.c
> +  Watchdog.h
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  ArmPlatformPkg/ArmPlatformPkg.dec
> +  EmbeddedPkg/EmbeddedPkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
> +  Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  FlashLib
> +  IoLib
> +  NVParamLib
> +  TimerLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Pcd]
> +  gArmTokenSpaceGuid.PcdGenericWatchdogControlBase
> +  gArmTokenSpaceGuid.PcdGenericWatchdogEl2IntrNum
> +
> +[Protocols]
> +  gEfiWatchdogTimerArchProtocolGuid             ## PRODUCES
> +  gHardwareInterrupt2ProtocolGuid               ## CONSUMES
> +
> +[Depex]
> +  gHardwareInterrupt2ProtocolGuid
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafe.h 
> b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafe.h
> new file mode 100644
> index 000000000000..911b093dce28
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafe.h
> @@ -0,0 +1,44 @@
> +/** @file
> +
> +  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef FAILSAFE_H_
> +#define FAILSAFE_H_
> +
> +#define FAILSAFE_BOOT_NORMAL                0x00
> +#define FAILSAFE_BOOT_LAST_KNOWN_SETTINGS   0x01
> +#define FAILSAFE_BOOT_DEFAULT_SETTINGS      0x02
> +#define FAILSAFE_BOOT_DDR_DOWNGRADE         0x03
> +#define FAILSAFE_BOOT_SUCCESSFUL            0x04
> +
> +#pragma pack(1)
> +typedef struct {
> +  UINT8  ImgMajorVer;
> +  UINT8  ImgMinorVer;
> +  UINT32 NumRetry1;
> +  UINT32 NumRetry2;
> +  UINT32 MaxRetry;
> +  UINT8  Status;
> +  //
> +  // Byte[3]: Reserved
> +  // Byte[2]: Slave MCU Failure Mask
> +  // Byte[1]: Reserved
> +  // Byte[0]: Master MCU Failure Mask
> +  //
> +  UINT32 MCUFailsMask;
> +  UINT16 CRC16;
> +  UINT8  Reserved[3];
> +} FAIL_SAFE_CONTEXT;
> +#pragma pack()
> +
> +BOOLEAN
> +EFIAPI
> +IsFailSafeOff (
> +  VOID
> +  );
> +
> +#endif /* FAILSAFE_H_ */
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.h 
> b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.h
> new file mode 100644
> index 000000000000..6c9106fdbea5
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.h
> @@ -0,0 +1,29 @@
> +/** @file
> +
> +  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef GENERIC_WATCHDOG_H_
> +#define GENERIC_WATCHDOG_H_
> +
> +#include <Protocol/WatchdogTimer.h>
> +
> +/* The number of 100ns periods (the unit of time passed to these functions)
> +   in a second */
> +#define TIME_UNITS_PER_SECOND           10000000
> +
> +/**
> +  The function to install Watchdog timer protocol to the system
> +
> +  @retval  Return         EFI_SUCCESS if install Watchdog timer protocol 
> successfully.
> + **/
> +EFI_STATUS
> +EFIAPI
> +WatchdogTimerInstallProtocol (
> +  EFI_WATCHDOG_TIMER_ARCH_PROTOCOL **WatchdogTimerProtocol
> +  );
> +
> +#endif /* GENERIC_WATCHDOG_H_ */
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.c 
> b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.c
> new file mode 100644
> index 000000000000..487e0d3870ab
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/FailSafeDxe.c
> @@ -0,0 +1,243 @@
> +/** @file
> +
> +  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Uefi.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/FlashLib.h>
> +#include <Library/NVParamLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +#include <Library/UefiRuntimeServicesTableLib.h>
> +#include <NVParamDef.h>
> +
> +#include "FailSafe.h"
> +#include "Watchdog.h"
> +
> +STATIC UINTN                            gWatchdogOSTimeout;
> +STATIC BOOLEAN                          gFailSafeOff;
> +STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *gWatchdogTimer;
> +
> +STATIC
> +INTN
> +CheckCrc16 (
> +  UINT8 *Pointer,
> +  INTN  Count
> +  )
> +{
> +  INTN Crc = 0;
> +  INTN Index;
> +
> +  while (--Count >= 0) {
> +    Crc = Crc ^ (INTN)*Pointer++ << 8;
> +    for (Index = 0; Index < 8; ++Index) {
> +      if ((Crc & 0x8000) != 0) {
> +        Crc = Crc << 1 ^ 0x1021;
> +      } else {
> +        Crc = Crc << 1;
> +      }
> +    }
> +  }
> +
> +  return Crc & 0xFFFF;
> +}
> +
> +BOOLEAN
> +FailSafeValidCRC (
> +  FAIL_SAFE_CONTEXT *FailSafeBuf
> +  )
> +{
> +  UINT8  Valid;
> +  UINT16 Crc;
> +  UINT32 Len;
> +
> +  Len = sizeof (FAIL_SAFE_CONTEXT);
> +  Crc = FailSafeBuf->CRC16;
> +  FailSafeBuf->CRC16 = 0;
> +
> +  Valid = (Crc == CheckCrc16 ((UINT8 *)FailSafeBuf, Len));
> +  FailSafeBuf->CRC16 = Crc;
> +
> +  return Valid;
> +}
> +
> +BOOLEAN
> +FailSafeFailureStatus (
> +  UINT8 Status
> +  )
> +{
> +  if ((Status == FAILSAFE_BOOT_LAST_KNOWN_SETTINGS) ||
> +      (Status == FAILSAFE_BOOT_DEFAULT_SETTINGS) ||
> +      (Status == FAILSAFE_BOOT_DDR_DOWNGRADE)) {
> +    return TRUE;
> +  }
> +
> +  return FALSE;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +FailSafeBootSuccessfully (
> +  VOID
> +  )
> +{
> +  EFI_STATUS                    Status;
> +  FAIL_SAFE_CONTEXT             FailSafeBuf;
> +  UINT32                        FailSafeSize;
> +  UINT64                        FailSafeStartOffset;
> +
> +  Status = FlashGetFailSafeInfo (&FailSafeStartOffset, &FailSafeSize);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "%a: Failed to get context region information\n", 
> __FUNCTION__));
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  Status = FlashReadCommand (FailSafeStartOffset, (UINT8 *)&FailSafeBuf, 
> sizeof (FAIL_SAFE_CONTEXT));
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  //
> +  // If failsafe context is valid, and:
> +  //    - The status indicate non-failure, then don't clear it
> +  //    - The status indicate a failure, then go and clear it
> +  //
> +  if (FailSafeValidCRC (&FailSafeBuf)
> +      && !FailSafeFailureStatus (FailSafeBuf.Status)) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  Status = FlashEraseCommand (FailSafeStartOffset, FailSafeSize);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +FailSafeTestBootFailure (
> +  VOID
> +  )
> +{
> +  EFI_STATUS Status;
> +  UINT32     Value = 0;
> +
> +  //
> +  // Simulate UEFI boot failure due to config wrong NVPARAM for
> +  // testing failsafe feature
> +  //
> +  Status = NVParamGet (NV_SI_UEFI_FAILURE_FAILSAFE, NV_PERM_ALL, &Value);
> +  if (!EFI_ERROR (Status) && (Value == 1)) {
> +    CpuDeadLoop ();
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +VOID
> +FailSafeTurnOff (
> +  VOID
> +  )
> +{
> +  EFI_STATUS Status;
> +
> +  if (IsFailSafeOff ()) {
> +    return;
> +  }
> +
> +  Status = FailSafeBootSuccessfully ();
> +  ASSERT_EFI_ERROR (Status);
> +
> +  gFailSafeOff = TRUE;
> +
> +  /* Disable Watchdog timer */
> +  gWatchdogTimer->SetTimerPeriod (gWatchdogTimer, 0);
> +}
> +
> +BOOLEAN
> +EFIAPI
> +IsFailSafeOff (
> +  VOID
> +  )
> +{
> +  return gFailSafeOff;
> +}
> +
> +/**
> +  The function to refresh Watchdog timer in the event before exiting boot 
> services
> +**/
> +VOID
> +WdtTimerExitBootServiceCallback (
> +  IN EFI_EVENT Event,
> +  IN VOID      *Context
> +  )
> +{
> +
> +  /* Enable Watchdog timer for OS booting */
> +  if (gWatchdogOSTimeout != 0) {
> +    gWatchdogTimer->SetTimerPeriod (
> +                      gWatchdogTimer,
> +                      gWatchdogOSTimeout * TIME_UNITS_PER_SECOND
> +                      );
> +  } else {
> +    /* Disable Watchdog timer */
> +    gWatchdogTimer->SetTimerPeriod (gWatchdogTimer, 0);
> +  }
> +}
> +
> +/**
> +  Main entry for this driver.
> +
> +  @param ImageHandle     Image handle this driver.
> +  @param SystemTable     Pointer to SystemTable.
> +
> +  @retval EFI_SUCCESS    This function always complete successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +FailSafeDxeEntryPoint (
> +  IN EFI_HANDLE       ImageHandle,
> +  IN EFI_SYSTEM_TABLE *SystemTable
> +  )
> +{
> +  EFI_EVENT  ExitBootServicesEvent;
> +  EFI_STATUS Status;
> +
> +  gFailSafeOff = FALSE;
> +
> +  FailSafeTestBootFailure ();
> +
> +  /* We need to setup non secure Watchdog to ensure that the system will
> +   * boot to OS successfully.
> +   *
> +   * The BIOS doesn't handle Watchdog interrupt so we expect WS1 asserted EL3
> +   * when Watchdog timeout triggered
> +   */
> +
> +  Status = WatchdogTimerInstallProtocol (&gWatchdogTimer);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  // We should register a callback function before entering to Setup screen
> +  // rather than always call it at DXE phase.
> +  FailSafeTurnOff ();
> +
> +  /* Register event before exit boot services */
> +  Status = gBS->CreateEvent (
> +                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
> +                  TPL_NOTIFY,
> +                  WdtTimerExitBootServiceCallback,
> +                  NULL,
> +                  &ExitBootServicesEvent
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.c 
> b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.c
> new file mode 100644
> index 000000000000..34329d04206a
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/FailSafeDxe/Watchdog.c
> @@ -0,0 +1,357 @@
> +/** @file
> +
> +  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/ArmGenericTimerCounterLib.h>
> +#include <Library/ArmLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiRuntimeServicesTableLib.h>
> +#include <Protocol/HardwareInterrupt2.h>
> +
> +#include "FailSafe.h"
> +#include "Watchdog.h"
> +
> +/* Watchdog timer controller registers */
> +#define WDT_CTRL_BASE_REG                     FixedPcdGet64 
> (PcdGenericWatchdogControlBase)
> +#define WDT_CTRL_WCS_OFF                      0x0
> +#define WDT_CTRL_WCS_ENABLE_MASK              0x1
> +#define WDT_CTRL_WOR_OFF                      0x8
> +#define WDT_CTRL_WCV_OFF                      0x10
> +#define WS0_INTERRUPT_SOURCE                  FixedPcdGet32 
> (PcdGenericWatchdogEl2IntrNum)
> +
> +STATIC UINT64                           mNumTimerTicks;
> +STATIC EFI_HARDWARE_INTERRUPT2_PROTOCOL *mInterruptProtocol;
> +BOOLEAN                                 mInterruptWS0Enabled;
> +
> +STATIC
> +VOID
> +WatchdogTimerWriteOffsetRegister (
> +  UINT32 Value
> +  )
> +{
> +  MmioWrite32 (WDT_CTRL_BASE_REG + WDT_CTRL_WOR_OFF, Value);
> +}
> +
> +STATIC
> +VOID
> +WatchdogTimerWriteCompareRegister (
> +  UINT64 Value
> +  )
> +{
> +  MmioWrite64 (WDT_CTRL_BASE_REG + WDT_CTRL_WCV_OFF, Value);
> +}
> +
> +STATIC
> +EFI_STATUS
> +WatchdogTimerEnable (
> +  IN BOOLEAN Enable
> +  )
> +{
> +  UINT32 Val =  MmioRead32 ((UINTN)(WDT_CTRL_BASE_REG + WDT_CTRL_WCS_OFF));
> +
> +  if (Enable) {
> +    Val |= WDT_CTRL_WCS_ENABLE_MASK;
> +  } else {
> +    Val &= ~WDT_CTRL_WCS_ENABLE_MASK;
> +  }
> +  MmioWrite32 ((UINTN)(WDT_CTRL_BASE_REG + WDT_CTRL_WCS_OFF), Val);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +STATIC
> +EFI_STATUS
> +WatchdogTimerSetup (
> +  VOID
> +  )
> +{
> +  EFI_STATUS Status;
> +
> +  /* Disable Watchdog timer */
> +  WatchdogTimerEnable (FALSE);
> +
> +  if (!mInterruptWS0Enabled) {
> +    Status = mInterruptProtocol->EnableInterruptSource (
> +                                   mInterruptProtocol,
> +                                   WS0_INTERRUPT_SOURCE
> +                                   );
> +    ASSERT_EFI_ERROR (Status);
> +
> +    mInterruptWS0Enabled = TRUE;
> +  }
> +
> +  if (mNumTimerTicks == 0) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  /* If the number of required ticks is greater than the max the Watchdog's
> +     offset register (WOR) can hold, we need to manually compute and set
> +     the compare register (WCV) */
> +  if (mNumTimerTicks > MAX_UINT32) {
> +    /* We need to enable the Watchdog *before* writing to the compare 
> register,
> +       because enabling the Watchdog causes an "explicit refresh", which
> +       clobbers the compare register (WCV). In order to make sure this 
> doesn't
> +       trigger an interrupt, set the offset to max. */
> +    WatchdogTimerWriteOffsetRegister (MAX_UINT32);
> +    WatchdogTimerEnable (TRUE);
> +    WatchdogTimerWriteCompareRegister (ArmGenericTimerGetSystemCount () + 
> mNumTimerTicks);
> +  } else {
> +    WatchdogTimerWriteOffsetRegister ((UINT32)mNumTimerTicks);
> +    WatchdogTimerEnable (TRUE);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +
> +/* This function is called when the Watchdog's first signal (WS0) goes high.
> +   It uses the ResetSystem Runtime Service to reset the board.
> +*/
> +VOID
> +EFIAPI
> +WatchdogTimerInterruptHandler (
> +  IN HARDWARE_INTERRUPT_SOURCE Source,
> +  IN EFI_SYSTEM_CONTEXT        SystemContext
> +  )
> +{
> +  STATIC CONST CHAR16 ResetString[]= L"The generic Watchdog timer ran out.";
> +
> +  mInterruptProtocol->EndOfInterrupt (mInterruptProtocol, Source);
> +
> +  if (!IsFailSafeOff ()) {
> +    /* Not handling interrupt as ATF is monitoring it */
> +    return;
> +  }
> +
> +  WatchdogTimerEnable (FALSE);
> +
> +  gRT->ResetSystem (
> +         EfiResetCold,
> +         EFI_TIMEOUT,
> +         StrSize (ResetString),
> +         (VOID *)&ResetString
> +         );
> +
> +  /* If we got here then the reset didn't work */
> +  ASSERT (FALSE);
> +}
> +
> +/**
> +  This function registers the handler NotifyFunction so it is called every 
> time
> +  the Watchdog timer expires.  It also passes the amount of time since the 
> last
> +  handler call to the NotifyFunction.
> +  If NotifyFunction is not NULL and a handler is not already registered,
> +  then the new handler is registered and EFI_SUCCESS is returned.
> +  If NotifyFunction is NULL, and a handler is already registered,
> +  then that handler is unregistered.
> +  If an attempt is made to register a handler when a handler is already
> +  registered, then EFI_ALREADY_STARTED is returned.
> +  If an attempt is made to unregister a handler when a handler is not
> +  registered, then EFI_INVALID_PARAMETER is returned.
> +
> +  @param  This             The EFI_TIMER_ARCH_PROTOCOL instance.
> +  @param  NotifyFunction   The function to call when a timer interrupt fires.
> +                           This function executes at TPL_HIGH_LEVEL. The DXE
> +                           Core will register a handler for the timer 
> interrupt,
> +                           so it can know how much time has passed. This
> +                           information is used to signal timer based events.
> +                           NULL will unregister the handler.
> +
> +  @retval EFI_UNSUPPORTED       The code does not support NotifyFunction.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +WatchdogTimerRegisterHandler (
> +  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This,
> +  IN       EFI_WATCHDOG_TIMER_NOTIFY        NotifyFunction
> +  )
> +{
> +  /* Not support. Watchdog will reset the board */
> +  return EFI_UNSUPPORTED;

What you actually have here is a hardware watchdog. On timeout it
triggers a hardware reset.

The definition of EFI_WATCHDOG_TIMER_ARCH_PROTOCOL is explicitly
described in PI (1.7a) as "This protocol is used to implement the Boot
Service SetWatchdogTimer().".

Further down the definition, the following text
---
When the watchdog timer fires, control will be passed to a handler if
one has been registered. If no handler has been registered, or the
registered handler returns, then the system will be reset by calling
the Runtime Service ResetSystem().
---
means that a watchdog that triggers a hardware reset on timeout is
inappropriate as the back-end for this. It cannot fulfill the
requirements of this protocol.

I see nothing wrong with including a driver for this hardware watchdog
in your platform port, but:
- It should be a standalone driver.
- It should not register itself as an implementation of
  EFI_WATCHDOG_TIMER_ARCH_PROTOCOL.
- The platform port will still need to include an *actual*
  implementation of EFI_WATCHDOG_TIMER_ARCH_PROTOCOL.

/
    Leif

> +}
> +
> +/**
> +  This function sets the amount of time to wait before firing the Watchdog
> +  timer to TimerPeriod 100ns units.  If TimerPeriod is 0, then the Watchdog
> +  timer is disabled.
> +
> +  @param  This             The EFI_WATCHDOG_TIMER_ARCH_PROTOCOL instance.
> +  @param  TimerPeriod      The amount of time in 100ns units to wait before
> +                           the Watchdog timer is fired. If TimerPeriod is 
> zero,
> +                           then the Watchdog timer is disabled.
> +
> +  @retval EFI_SUCCESS           The Watchdog timer has been programmed to 
> fire
> +                                in Time  100ns units.
> +  @retval EFI_DEVICE_ERROR      A Watchdog timer could not be programmed due
> +                                to a device error.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +WatchdogTimerSetPeriod (
> +  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This,
> +  IN       UINT64                           TimerPeriod   // In 100ns units
> +  )
> +{
> +  mNumTimerTicks  = (ArmGenericTimerGetTimerFreq () * TimerPeriod) / 
> TIME_UNITS_PER_SECOND;
> +
> +  if (!IsFailSafeOff ()) {
> +    /* Not support Watchdog timer service until FailSafe is off as ATF is 
> monitoring it */
> +    return EFI_SUCCESS;
> +  }
> +
> +  return WatchdogTimerSetup ();
> +}
> +
> +/**
> +  This function retrieves the period of timer interrupts in 100ns units,
> +  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
> +  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
> +  returned, then the timer is currently disabled.
> +
> +  @param  This             The EFI_TIMER_ARCH_PROTOCOL instance.
> +  @param  TimerPeriod      A pointer to the timer period to retrieve in
> +                           100ns units. If 0 is returned, then the timer is
> +                           currently disabled.
> +
> +
> +  @retval EFI_SUCCESS           The timer period was returned in TimerPeriod.
> +  @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +WatchdogTimerGetPeriod (
> +  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This,
> +  OUT      UINT64                           *TimerPeriod
> +  )
> +{
> +  if (TimerPeriod == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  *TimerPeriod = ((TIME_UNITS_PER_SECOND / ArmGenericTimerGetTimerFreq ()) * 
> mNumTimerTicks);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Interface structure for the Watchdog Architectural Protocol.
> +
> +  @par Protocol Description:
> +  This protocol provides a service to set the amount of time to wait
> +  before firing the Watchdog timer, and it also provides a service to
> +  register a handler that is invoked when the Watchdog timer fires.
> +
> +  @par When the Watchdog timer fires, control will be passed to a handler
> +  if one has been registered.  If no handler has been registered,
> +  or the registered handler returns, then the system will be
> +  reset by calling the Runtime Service ResetSystem().
> +
> +  @param RegisterHandler
> +  Registers a handler that will be called each time the
> +  Watchdogtimer interrupt fires.  TimerPeriod defines the minimum
> +  time between timer interrupts, so TimerPeriod will also
> +  be the minimum time between calls to the registered
> +  handler.
> +  NOTE: If the Watchdog resets the system in hardware, then
> +        this function will not have any chance of executing.
> +
> +  @param SetTimerPeriod
> +  Sets the period of the timer interrupt in 100ns units.
> +  This function is optional, and may return EFI_UNSUPPORTED.
> +  If this function is supported, then the timer period will
> +  be rounded up to the nearest supported timer period.
> +
> +  @param GetTimerPeriod
> +  Retrieves the period of the timer interrupt in 100ns units.
> +
> +**/
> +STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL gWatchdogTimer = {
> +  (EFI_WATCHDOG_TIMER_REGISTER_HANDLER)WatchdogTimerRegisterHandler,
> +  (EFI_WATCHDOG_TIMER_SET_TIMER_PERIOD)WatchdogTimerSetPeriod,
> +  (EFI_WATCHDOG_TIMER_GET_TIMER_PERIOD)WatchdogTimerGetPeriod
> +};
> +
> +EFI_STATUS
> +EFIAPI
> +WatchdogTimerInstallProtocol (
> +  EFI_WATCHDOG_TIMER_ARCH_PROTOCOL **WatchdogTimerProtocol
> +  )
> +{
> +  EFI_STATUS Status;
> +  EFI_HANDLE Handle;
> +  EFI_TPL    CurrentTpl;
> +
> +  /* Make sure the Watchdog Timer Architectural Protocol has not been 
> installed
> +     in the system yet.
> +     This will avoid conflicts with the universal Watchdog */
> +  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, 
> &gEfiWatchdogTimerArchProtocolGuid);
> +
> +  ASSERT (ArmGenericTimerGetTimerFreq () != 0);
> +
> +  /* Install interrupt handler */
> +  Status = gBS->LocateProtocol (
> +                  &gHardwareInterrupt2ProtocolGuid,
> +                  NULL,
> +                  (VOID **)&mInterruptProtocol
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  /*
> +   * We don't want to be interrupted while registering Watchdog interrupt 
> source as the interrupt
> +   * may be trigger in the middle because the interrupt line already enabled 
> in the EL3.
> +   */
> +  CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
> +
> +  Status = mInterruptProtocol->RegisterInterruptSource (
> +                                 mInterruptProtocol,
> +                                 WS0_INTERRUPT_SOURCE,
> +                                 WatchdogTimerInterruptHandler
> +                                 );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  /* Don't enable interrupt until FailSafe off */
> +  mInterruptWS0Enabled = FALSE;
> +  Status = mInterruptProtocol->DisableInterruptSource (
> +                                 mInterruptProtocol,
> +                                 WS0_INTERRUPT_SOURCE
> +                                 );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  gBS->RestoreTPL (CurrentTpl);
> +
> +  Status = mInterruptProtocol->SetTriggerType (
> +                                 mInterruptProtocol,
> +                                 WS0_INTERRUPT_SOURCE,
> +                                 EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH
> +                                 );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  /* Install the Timer Architectural Protocol onto a new handle */
> +  Handle = NULL;
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &Handle,
> +                  &gEfiWatchdogTimerArchProtocolGuid,
> +                  &gWatchdogTimer,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  mNumTimerTicks = 0;
> +
> +  if (WatchdogTimerProtocol != NULL) {
> +    *WatchdogTimerProtocol = &gWatchdogTimer;
> +  }
> +
> +  return Status;
> +}
> -- 
> 2.17.1
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#82684): https://edk2.groups.io/g/devel/message/82684
Mute This Topic: https://groups.io/mt/86507913/21656
Group Owner: [email protected]
Unsubscribe: https://edk2.groups.io/g/devel/unsub [[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to