diff --git a/EmbeddedPkg/EmbeddedPkg.dsc b/EmbeddedPkg/EmbeddedPkg.dsc index 4d9e6399d5..dc5040e611 100644 --- a/EmbeddedPkg/EmbeddedPkg.dsc +++ b/EmbeddedPkg/EmbeddedPkg.dsc @@ -218,6 +218,7 @@ EmbeddedPkg/Library/CoherentDmaLib/CoherentDmaLib.inf EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf EmbeddedPkg/Library/DxeDtPlatformDtbLoaderLibDefault/DxeDtPlatformDtbLoaderLibDefault.inf + EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf EmbeddedPkg/EmbeddedMonotonicCounter/EmbeddedMonotonicCounter.inf EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf diff --git a/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c new file mode 100644 index 0000000000..4c354730d0 --- /dev/null +++ b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c @@ -0,0 +1,400 @@ +/** @file + * + * Implement virtual EFI RealTimeClock runtime services. + * + * Coypright (c) 2019, Pete Batard + * Copyright (c) 2018, Andrei Warkentin + * Copyright (c) 2011-2014, ARM Ltd. All rights reserved. + * Copyright (c) 2008-2010, Apple Inc. All rights reserved. + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + * Based on ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.inf + * + **/ + +#include +#include +#include +#include +#include +#include +#include +#include + +STATIC CONST CHAR16 mEpochVariableName[] = L"RtcEpochSeconds"; +STATIC CONST CHAR16 mTimeZoneVariableName[] = L"RtcTimeZone"; +STATIC CONST CHAR16 mDaylightVariableName[] = L"RtcDaylight"; + +/** + Returns the current time and date information, and the time-keeping capabilities + of the virtual RTC. + + @param Time A pointer to storage to receive a snapshot of the current time. + @param Capabilities An optional pointer to a buffer to receive the real time clock + device's capabilities. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Time is NULL. + @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error. + +**/ +EFI_STATUS +EFIAPI +LibGetTime ( + OUT EFI_TIME *Time, + OUT EFI_TIME_CAPABILITIES *Capabilities + ) +{ + EFI_STATUS Status; + UINT32 EpochSeconds; + INT16 TimeZone; + UINT8 Daylight; + UINT64 Freq; + UINT64 Counter; + UINT64 Remainder; + UINTN ElapsedSeconds; + UINTN Size; + + if (Time == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Get the counter frequency + Freq = GetPerformanceCounterProperties (NULL, NULL); + if (Freq == 0) { + return EFI_DEVICE_ERROR; + } + + // Get the epoch time from non-volatile storage + Size = sizeof (UINTN); + ElapsedSeconds = 0; + Status = EfiGetVariable ( + (CHAR16 *)mEpochVariableName, + &gEfiCallerIdGuid, + NULL, + &Size, + (VOID *)&ElapsedSeconds + ); + // Fall back to compilation-time epoch if not set + if (EFI_ERROR (Status)) { + ASSERT(Status != EFI_INVALID_PARAMETER); + ASSERT(Status != EFI_BUFFER_TOO_SMALL); + // + // The following is intended to produce a compilation error on build + // environments where BUILD_EPOCH can not be set from inline shell. + // If you are attempting to use this library on such an environment, please + // contact the edk2 mailing list, so we can try to add support for it. + // + ElapsedSeconds = BUILD_EPOCH; + DEBUG (( + DEBUG_INFO, + "LibGetTime: %s non volatile variable was not found - Using compilation time epoch.\n", + mEpochVariableName + )); + } + Counter = GetPerformanceCounter (); + ElapsedSeconds += DivU64x64Remainder (Counter, Freq, &Remainder); + + // Get the current time zone information from non-volatile storage + Size = sizeof (TimeZone); + Status = EfiGetVariable ( + (CHAR16 *)mTimeZoneVariableName, + &gEfiCallerIdGuid, + NULL, + &Size, + (VOID *)&TimeZone + ); + + if (EFI_ERROR (Status)) { + ASSERT(Status != EFI_INVALID_PARAMETER); + ASSERT(Status != EFI_BUFFER_TOO_SMALL); + + if (Status != EFI_NOT_FOUND) { + return Status; + } + + // The time zone variable does not exist in non-volatile storage, so create it. + Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; + // Store it + Status = EfiSetVariable ( + (CHAR16 *)mTimeZoneVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + Size, + (VOID *)&(Time->TimeZone) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n", + mTimeZoneVariableName, + Status + )); + return Status; + } + } else { + // Got the time zone + Time->TimeZone = TimeZone; + + // Check TimeZone bounds: -1440 to 1440 or 2047 + if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440)) + && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) { + Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE; + } + + // Adjust for the correct time zone + if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { + EpochSeconds += Time->TimeZone * SEC_PER_MIN; + } + } + + // Get the current daylight information from non-volatile storage + Size = sizeof (Daylight); + Status = EfiGetVariable ( + (CHAR16 *)mDaylightVariableName, + &gEfiCallerIdGuid, + NULL, + &Size, + (VOID *)&Daylight + ); + + if (EFI_ERROR (Status)) { + ASSERT(Status != EFI_INVALID_PARAMETER); + ASSERT(Status != EFI_BUFFER_TOO_SMALL); + + if (Status != EFI_NOT_FOUND) { + return Status; + } + + // The daylight variable does not exist in non-volatile storage, so create it. + Time->Daylight = 0; + // Store it + Status = EfiSetVariable ( + (CHAR16 *)mDaylightVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + Size, + (VOID *)&(Time->Daylight) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n", + mDaylightVariableName, + Status + )); + return Status; + } + } else { + // Got the daylight information + Time->Daylight = Daylight; + + // Adjust for the correct period + if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) { + // Convert to adjusted time, i.e. spring forwards one hour + EpochSeconds += SEC_PER_HOUR; + } + } + + EpochToEfiTime (ElapsedSeconds, Time); + + // Because we use the performance counter, we can fill the Nanosecond attribute + // provided that the remainder doesn't overflow 64-bit during multiplication. + if (Remainder <= 18446744073U) { + Time->Nanosecond = MultU64x64 (Remainder, 1000000000U) / Freq; + } else { + DEBUG ((DEBUG_WARN, "LibGetTime: Nanosecond value not set (64-bit overflow).\n")); + } + + if (Capabilities) { + Capabilities->Accuracy = 0; + Capabilities->Resolution = Freq; + Capabilities->SetsToZero = FALSE; + } + + return EFI_SUCCESS; +} + +/** + Sets the current local time and date information. + + @param Time A pointer to the current time. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A time field is out of range. + @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error. + +**/ +EFI_STATUS +EFIAPI +LibSetTime ( + IN EFI_TIME *Time + ) +{ + EFI_STATUS Status; + UINTN EpochSeconds; + + if (!IsTimeValid (Time)) { + return EFI_INVALID_PARAMETER; + } + + EpochSeconds = EfiTimeToEpoch (Time); + + // Adjust for the correct time zone, i.e. convert to UTC time zone + if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) { + EpochSeconds -= Time->TimeZone * SEC_PER_MIN; + } + + // Adjust for the correct period + if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) { + // Convert to un-adjusted time, i.e. fall back one hour + EpochSeconds -= SEC_PER_HOUR; + } + + // Save the current time zone information into non-volatile storage + Status = EfiSetVariable ( + (CHAR16 *)mTimeZoneVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (Time->TimeZone), + (VOID *)&(Time->TimeZone) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n", + mTimeZoneVariableName, + Status + )); + return Status; + } + + // Save the current daylight information into non-volatile storage + Status = EfiSetVariable ( + (CHAR16 *)mDaylightVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(Time->Daylight), + (VOID *)&(Time->Daylight) + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n", + mDaylightVariableName, + Status + )); + return Status; + } + + Status = EfiSetVariable ( + (CHAR16 *)mEpochVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (EpochSeconds), + &EpochSeconds + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n", + mDaylightVariableName, + Status + )); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Returns the current wakeup alarm clock setting. + + @param Enabled Indicates if the alarm is currently enabled or disabled. + @param Pending Indicates if the alarm signal is pending and requires acknowledgement. + @param Time The current alarm setting. + + @retval EFI_SUCCESS The alarm settings were returned. + @retval EFI_INVALID_PARAMETER Any parameter is NULL. + @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +LibGetWakeupTime ( + OUT BOOLEAN *Enabled, + OUT BOOLEAN *Pending, + OUT EFI_TIME *Time + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Sets the system wakeup alarm clock time. + + @param Enabled Enable or disable the wakeup alarm. + @param Time If Enable is TRUE, the time to set the wakeup alarm for. + + @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If + Enable is FALSE, then the wakeup alarm was disabled. + @retval EFI_INVALID_PARAMETER A time field is out of range. + @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error. + @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform. + +**/ +EFI_STATUS +EFIAPI +LibSetWakeupTime ( + IN BOOLEAN Enabled, + OUT EFI_TIME *Time + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This is the declaration of an EFI image entry point. This can be the entry point to an application + written to this specification, an EFI boot service driver, or an EFI runtime driver. + + @param ImageHandle Handle that identifies the loaded image. + @param SystemTable System Table for this image. + + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +EFIAPI +LibRtcInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_SUCCESS; +} + +/** + Fixup internal data so that EFI can be call in virtual mode. + Call the passed in Child Notify event and convert any pointers in + lib to virtual mode. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +VOID +EFIAPI +LibRtcVirtualNotifyEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + return; +} diff --git a/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf new file mode 100644 index 0000000000..ed69faad42 --- /dev/null +++ b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf @@ -0,0 +1,43 @@ +#/** @file +# +# Implement virtual EFI RealTimeClock runtime services. +# +# Copyright (c) 2019, Pete Batard +# Copyright (c) 2018, Andrei Warkentin +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +#**/ + +[Defines] + INF_VERSION = 0x0001001A + BASE_NAME = VirtualRealTimeClockLib + FILE_GUID = 1E27D461-78F3-4F7D-B1C2-F72384F13A6E + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = RealTimeClockLib + +[Sources.common] + VirtualRealTimeClockLib.c + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + +[LibraryClasses] + IoLib + DebugLib + TimerLib + TimeBaseLib + UefiRuntimeLib + +# Current usage of this library expects GCC in a UNIX-like shell environment with the date command +[BuildOptions] + GCC:*_*_*_CC_FLAGS = -DBUILD_EPOCH=`date +%s`