+ *
+ * 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 <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;
+ 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 000000000000..ed69faad4205
--- /dev/null
+++
b/EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf
@@ -0,0 +1,43 @@
+#/** @file
+#
+# Implement virtual EFI RealTimeClock runtime services.
+#
+# Copyright (c) 2019, Pete Batard <p...@akeo.ie>
+# Copyright (c) 2018, Andrei Warkentin <andrey.warken...@gmail.com>
+# Copyright (c) Microsoft Corporation. All rights reserved.