The RealTimeClockLib class header in edk2 mistakenly declares a function called LibRtcVirtualNotifyEvent(). No component ever calls this function crossing module boundaries; all RealTimeClockLib instances in edk2 and edk2-platforms are supposed to register (and do register) their SetVirtualAddressMap() notification functions. In particular, VirtualRealTimeClockLib *itself* doesn't even use LibRtcVirtualNotifyEvent() -- the function is defined with an empty body, clearly in an understandable, but misguided, attempt, to conform to the (bogus) library interface. Remove the function. Build-tested only (with "RiscVVirtQemu.dsc"). Cc: Abner Chang <abner.chang@amd.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Daniel Schaefer <git@danielschaefer.me> Cc: Leif Lindholm <quic_llindhol@quicinc.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=4564 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20231012091057.108728-5-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
		
			
				
	
	
		
			405 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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;
 | 
						|
}
 |