diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c new file mode 100644 index 00000000..c93bf408 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c @@ -0,0 +1,418 @@ +/** @file + * + * Implement virtual EFI RealTimeClock runtime services. + * + * Coypright (c) 2019, Pete Batard <pete@akeo.ie> + * Copyright (c) 2018, Andrei Warkentin <andrey.warkentin@gmail.com> + * 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. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + * + * Based on ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.inf + * + **/ + +#include <PiDxe.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/IoLib.h> +#include <Library/RealTimeClockLib.h> +#include <Library/TimerLib.h> +#include <Library/TimeBaseLib.h> +#include <Library/UefiRuntimeLib.h> + +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; + INT16 TimeZone; + UINT8 Daylight; + UINT64 Freq; + UINT64 Counter; + UINT64 Remainder; + UINTN EpochSeconds; + 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); + EpochSeconds = 0; + Status = EfiGetVariable ( + (CHAR16 *)mEpochVariableName, + &gEfiCallerIdGuid, + NULL, + &Size, + (VOID *)&EpochSeconds + ); + // 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. + // + EpochSeconds = BUILD_EPOCH; + DEBUG (( + DEBUG_INFO, + "LibGetTime: %s non volatile variable was not found - Using compilation time epoch.\n", + mEpochVariableName + )); + + EfiSetVariable ( + (CHAR16 *)mEpochVariableName, + &gEfiCallerIdGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (EpochSeconds), + &EpochSeconds + ); + } + Counter = GetPerformanceCounter (); + EpochSeconds += 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 (EpochSeconds, 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 = (UINT32)(MultU64x64 (Remainder, 1000000000U) / Freq); + } else { + DEBUG ((DEBUG_WARN, "LibGetTime: Nanosecond value not set (64-bit overflow).\n")); + } + + if (Capabilities) { + Capabilities->Accuracy = 0; + Capabilities->Resolution = 1; + 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; + UINT64 Freq; + UINT64 Counter; + UINT64 Remainder; + 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)) { + EpochSeconds -= Time->TimeZone * SEC_PER_MIN; + } + + // Adjust for the correct period + if (((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) + && (EpochSeconds > SEC_PER_HOUR)) { + // Convert to un-adjusted time, i.e. fall back one hour + EpochSeconds -= SEC_PER_HOUR; + } + + // Use the performance counter to subtract the number of seconds + // since platform reset. Without this, setting time from the shell + // and immediately reading it back would result in a forward time + // offset, of the duration during which the platform has been up. + Freq = GetPerformanceCounterProperties (NULL, NULL); + if (Freq != 0) { + Counter = GetPerformanceCounter (); + if (EpochSeconds > DivU64x64Remainder (Counter, Freq, &Remainder)) { + EpochSeconds -= DivU64x64Remainder (Counter, Freq, &Remainder); + } + } + + // 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; +} |