| /** @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-2021, 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_NON_VOLATILE | 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 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", | |
| mEpochVariableName, | |
| 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; | |
| } |