From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) by mx.groups.io with SMTP id smtpd.web10.11300.1635250557397136587 for ; Tue, 26 Oct 2021 05:15:58 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@nuviainc-com.20210112.gappssmtp.com header.s=20210112 header.b=ZmAeMONO; spf=pass (domain: nuviainc.com, ip: 209.85.221.47, mailfrom: leif@nuviainc.com) Received: by mail-wr1-f47.google.com with SMTP id z14so17694774wrg.6 for ; Tue, 26 Oct 2021 05:15:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nuviainc-com.20210112.gappssmtp.com; s=20210112; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=S8E2qgzipFKJ5qRetK4DBIkzMOH4vuHQg+6pajh+zV0=; b=ZmAeMONOxn0jJtBl81xCJ8utawlL9jlhJA6jpxf1BgUaFGbgszwhMVBzyTtVaexkj3 hr2LSwtwLUgCr1oXN45vlMLOP9v93tuolZGooWV6RBAnZJutXwM9MjGIKYYnMe4tavpD YP4fXQCt6POebmYojDONt8MvOrlao8sjm1DsegccjVbRq3Zviv2ukH8HfwOLfmZd/JJG 4aEqA8uEJA1eEP4UEpIw4JSqAQKTysM49FTNL2qpzs0Sep7wBK7O+6GeJ5yL8hOJIe6J K8UoBCcdg4Zz4S4nKhvQlv3taemb8v5F21h2Ps4bp3BAvN/DdpOTtDRXnAMkYjhmyWBp xSeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=S8E2qgzipFKJ5qRetK4DBIkzMOH4vuHQg+6pajh+zV0=; b=Ab/epM9rGLOKGHdTGjbfMkgNNtDKXa8FCEgOeuE2MTJaoFKNpyRGQoZzHa2t0qBsfH sH4DnHWLFmOQtwN8nZ/xpV3fZrQNVwmBFFtO6N4WASnuCr2Y6hS8fsrcOdZYlVIuFpOr aH93huSYJAc6j+sdvIDiJc1E9Cn+u6PX0Fsd7BgQHRI9acguB5iTjjvmwT6gHPWnTF6U kUoRwaZIf21T/vJbyJAVPAuljlbeLzk4lKCBP5QVn2l0JrzlfotXbBpoRRhRv5KIR674 FmWwnH3/mYilLvOgtElRuWX/CReaqtaSfAE9vIG/SZL72MTGEzNn1ywkZDzWD7hLQeUu A8Tw== X-Gm-Message-State: AOAM531WTUX6waVJaOC44W6lM/JuEMHDa75qp0opdSz6+yVtPJj3GnHI w8PFIVNWEgvIXlX4BMo6+lHecA== X-Google-Smtp-Source: ABdhPJwGshCMSKw8Juwb/ty52mXpocKIq6lsgMtbN99n9Pr5N796fT4lzZUOtqUI10Ufwu2B5VGxIA== X-Received: by 2002:adf:f1d2:: with SMTP id z18mr22354555wro.160.1635250555854; Tue, 26 Oct 2021 05:15:55 -0700 (PDT) Return-Path: Received: from leviathan (cpc92314-cmbg19-2-0-cust559.5-4.cable.virginm.net. [82.11.186.48]) by smtp.gmail.com with ESMTPSA id s3sm424156wmh.30.2021.10.26.05.15.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Oct 2021 05:15:55 -0700 (PDT) Date: Tue, 26 Oct 2021 13:15:53 +0100 From: "Leif Lindholm" To: Nhi Pham Cc: devel@edk2.groups.io, patches@amperecomputing.com, vunguyen@os.amperecomputing.com, Thang Nguyen , Chuong Tran , Phong Vo , Michael D Kinney , Ard Biesheuvel , Nate DeSimone Subject: Re: [edk2-platforms][PATCH v4 03/31] AmpereAltraPkg: Add FailSafe and WDT support Message-ID: <20211026121553.rm6l6dvoztjd3o3c@leviathan> References: <20211022061809.31087-1-nhi@os.amperecomputing.com> <20211022061809.31087-4-nhi@os.amperecomputing.com> MIME-Version: 1.0 In-Reply-To: <20211022061809.31087-4-nhi@os.amperecomputing.com> Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 > Cc: Chuong Tran > Cc: Phong Vo > Cc: Leif Lindholm > Cc: Michael D Kinney > Cc: Ard Biesheuvel > Cc: Nate DeSimone > > Signed-off-by: Nhi Pham > Reviewed-by: Leif Lindholm > --- > 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.
> +# > +# 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.
> + > + 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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef GENERIC_WATCHDOG_H_ > +#define GENERIC_WATCHDOG_H_ > + > +#include > + > +/* 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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 >