Home | History | Annotate | Download | only in VirtualRealTimeClockLib
      1 /** @file
      2   Implement EFI RealTimeClock runtime services via RTC Lib.
      3 
      4   Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
      5   Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
      6   Copyright (c) 2015, Hisilicon Limited. All rights reserved.<BR>
      7   Copyright (c) 2015, Linaro Limited. All rights reserved.<BR>
      8 
      9   This program and the accompanying materials
     10   are licensed and made available under the terms and conditions of the BSD License
     11   which accompanies this distribution.  The full text of the license may be found at
     12   http://opensource.org/licenses/bsd-license.php
     13 
     14   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     15   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     16 
     17   Based on the files under ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.inf
     18 
     19 **/
     20 
     21 #include <Uefi.h>
     22 #include <PiDxe.h>
     23 #include <Library/BaseLib.h>
     24 #include <Library/DebugLib.h>
     25 #include <Library/UefiLib.h>
     26 #include <Library/IoLib.h>
     27 #include <Library/RealTimeClockLib.h>
     28 #include <Library/MemoryAllocationLib.h>
     29 #include <Library/PcdLib.h>
     30 #include <Library/DxeServicesTableLib.h>
     31 #include <Library/UefiBootServicesTableLib.h>
     32 #include <Library/UefiRuntimeServicesTableLib.h>
     33 #include <Library/UefiRuntimeLib.h>
     34 #include <Library/EfiTimeBaseLib.h>
     35 
     36 #include <Protocol/RealTimeClock.h>
     37 
     38 #include <Guid/GlobalVariable.h>
     39 #include <Guid/EventGroup.h>
     40 
     41 #include <Library/ArmArchTimer.h>
     42 
     43 STATIC CONST CHAR16           mTimeZoneVariableName[] = L"PV660VirtualRtcTimeZone";
     44 STATIC CONST CHAR16           mDaylightVariableName[] = L"PV660VirtualRtcDaylight";
     45 STATIC EFI_EVENT              mRtcVirtualAddrChangeEvent;
     46 STATIC EFI_RUNTIME_SERVICES   *mRT;
     47 
     48 
     49 STATIC INTN mEpochDiff = 0;
     50 
     51 /**
     52   Returns the current time and date information, and the time-keeping capabilities
     53   of the hardware platform.
     54 
     55   @param  Time                   A pointer to storage to receive a snapshot of the current time.
     56   @param  Capabilities           An optional pointer to a buffer to receive the real time clock
     57                                  device's capabilities.
     58 
     59   @retval EFI_SUCCESS            The operation completed successfully.
     60   @retval EFI_INVALID_PARAMETER  Time is NULL.
     61   @retval EFI_DEVICE_ERROR       The time could not be retrieved due to hardware error.
     62   @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
     63 **/
     64 EFI_STATUS
     65 EFIAPI
     66 LibGetTime (
     67   OUT EFI_TIME                *Time,
     68   OUT EFI_TIME_CAPABILITIES   *Capabilities
     69   )
     70 {
     71   EFI_STATUS  Status = EFI_SUCCESS;
     72   UINT64      Temp;
     73   UINT32      EpochSeconds;
     74   INT16       TimeZone = 0;
     75   UINT8       Daylight = 0;
     76   UINTN       Size;
     77 
     78   // Ensure Time is a valid pointer
     79   if (Time == NULL) {
     80     return EFI_INVALID_PARAMETER;
     81   }
     82 
     83   ArmArchTimerReadReg(CntPct,&Temp);
     84 
     85   // UINT32 force convertion for PC-LINT
     86   EpochSeconds = mEpochDiff + Temp / (UINT32) PcdGet32(PcdArmArchTimerFreqInHz);
     87 
     88   // Get the current time zone information from non-volatile storage
     89   Size = sizeof (TimeZone);
     90   Status = mRT->GetVariable (
     91                   (CHAR16 *)mTimeZoneVariableName,
     92                   &gEfiCallerIdGuid,
     93                   NULL,
     94                   &Size,
     95                   (VOID *)&TimeZone
     96                   );
     97 
     98   if (EFI_ERROR (Status)) {
     99     ASSERT(Status != EFI_INVALID_PARAMETER);
    100     ASSERT(Status != EFI_BUFFER_TOO_SMALL);
    101 
    102     if (Status != EFI_NOT_FOUND)
    103       goto EXIT;
    104 
    105     // The time zone variable does not exist in non-volatile storage, so create it.
    106     Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
    107     // Store it
    108     Status = mRT->SetVariable (
    109                     (CHAR16 *)mTimeZoneVariableName,
    110                     &gEfiCallerIdGuid,
    111                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    112                     Size,
    113                     (VOID *)&(Time->TimeZone)
    114                     );
    115     if (EFI_ERROR (Status)) {
    116       DEBUG ((
    117         EFI_D_ERROR,
    118         "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    119         mTimeZoneVariableName,
    120         Status
    121         ));
    122       goto EXIT;
    123     }
    124   } else {
    125     // Got the time zone
    126     Time->TimeZone = TimeZone;
    127 
    128     // Check TimeZone bounds:   -1440 to 1440 or 2047
    129     if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440))
    130         && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {
    131       Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
    132     }
    133 
    134     // Adjust for the correct time zone
    135     if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
    136       EpochSeconds += Time->TimeZone * SEC_PER_MIN;
    137     }
    138   }
    139 
    140   // Get the current daylight information from non-volatile storage
    141   Size = sizeof (Daylight);
    142   Status = mRT->GetVariable (
    143                   (CHAR16 *)mDaylightVariableName,
    144                   &gEfiCallerIdGuid,
    145                   NULL,
    146                   &Size,
    147                   (VOID *)&Daylight
    148                   );
    149 
    150   if (EFI_ERROR (Status)) {
    151     ASSERT(Status != EFI_INVALID_PARAMETER);
    152     ASSERT(Status != EFI_BUFFER_TOO_SMALL);
    153 
    154     if (Status != EFI_NOT_FOUND)
    155       goto EXIT;
    156 
    157     // The daylight variable does not exist in non-volatile storage, so create it.
    158     Time->Daylight = 0;
    159     // Store it
    160     Status = mRT->SetVariable (
    161                     (CHAR16 *)mDaylightVariableName,
    162                     &gEfiCallerIdGuid,
    163                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    164                     Size,
    165                     (VOID *)&(Time->Daylight)
    166                     );
    167     if (EFI_ERROR (Status)) {
    168       DEBUG ((
    169         EFI_D_ERROR,
    170         "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    171         mDaylightVariableName,
    172         Status
    173         ));
    174       goto EXIT;
    175     }
    176   } else {
    177     // Got the daylight information
    178     Time->Daylight = Daylight;
    179 
    180     // Adjust for the correct period
    181     if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
    182       // Convert to adjusted time, i.e. spring forwards one hour
    183       EpochSeconds += SEC_PER_HOUR;
    184     }
    185   }
    186 
    187   // Convert from internal 32-bit time to UEFI time
    188   EpochToEfiTime (EpochSeconds, Time);
    189 
    190   // Update the Capabilities info
    191   if (Capabilities != NULL) {
    192     Capabilities->Resolution  = 1;
    193     // Accuracy in ppm multiplied by 1,000,000, e.g. for 50ppm set 50,000,000
    194 
    195     Capabilities->Accuracy    = PcdGet32 (PcdArmArchTimerFreqInHz);
    196     // FALSE: Setting the time does not clear the values below the resolution level
    197     Capabilities->SetsToZero  = FALSE;
    198   }
    199 
    200   EXIT:
    201   return Status;
    202 }
    203 
    204 
    205 /**
    206   Sets the current local time and date information.
    207 
    208   @param  Time                  A pointer to the current time.
    209 
    210   @retval EFI_SUCCESS           The operation completed successfully.
    211   @retval EFI_INVALID_PARAMETER A time field is out of range.
    212   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
    213 
    214 **/
    215 EFI_STATUS
    216 EFIAPI
    217 LibSetTime (
    218   IN  EFI_TIME                *Time
    219   )
    220 {
    221   EFI_STATUS  Status;
    222   UINTN       EpochSeconds;
    223   UINTN       Temp;
    224 
    225   // Check the input parameters are within the range specified by UEFI
    226   if (!IsTimeValid (Time)) {
    227     Status = EFI_INVALID_PARAMETER;
    228     goto EXIT;
    229   }
    230 
    231   // Because the PL031 is a 32-bit counter counting seconds,
    232   // the maximum time span is just over 136 years.
    233   // Time is stored in Unix Epoch format, so it starts in 1970,
    234   // Therefore it can not exceed the year 2106.
    235   if ((Time->Year < 1970) || (Time->Year >= 2106)) {
    236     Status = EFI_UNSUPPORTED;
    237     goto EXIT;
    238   }
    239 
    240   EpochSeconds = EfiTimeToEpoch (Time);
    241 
    242   // Adjust for the correct time zone, i.e. convert to UTC time zone
    243   if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
    244     EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
    245   }
    246 
    247   // TODO: Automatic Daylight activation
    248 
    249   // Adjust for the correct period
    250   if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
    251     // Convert to un-adjusted time, i.e. fall back one hour
    252     EpochSeconds -= SEC_PER_HOUR;
    253   }
    254 
    255   ArmArchTimerReadReg(CntPct,&Temp);
    256 
    257   // UINT32 force convertion for PC-LINT
    258   mEpochDiff = EpochSeconds - Temp / (UINT32) PcdGet32(PcdArmArchTimerFreqInHz);
    259 
    260   // The accesses to Variable Services can be very slow, because we may be writing to Flash.
    261   // Do this after having set the RTC.
    262 
    263   // Save the current time zone information into non-volatile storage
    264   Status = mRT->SetVariable (
    265                   (CHAR16 *)mTimeZoneVariableName,
    266                   &gEfiCallerIdGuid,
    267                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    268                   sizeof (Time->TimeZone),
    269                   (VOID *)&(Time->TimeZone)
    270                   );
    271   if (EFI_ERROR (Status)) {
    272       DEBUG ((
    273         EFI_D_ERROR,
    274         "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    275         mTimeZoneVariableName,
    276         Status
    277         ));
    278     goto EXIT;
    279   }
    280 
    281   // Save the current daylight information into non-volatile storage
    282   Status = mRT->SetVariable (
    283                   (CHAR16 *)mDaylightVariableName,
    284                   &gEfiCallerIdGuid,
    285                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    286                   sizeof(Time->Daylight),
    287                   (VOID *)&(Time->Daylight)
    288                   );
    289   if (EFI_ERROR (Status)) {
    290     DEBUG ((
    291       EFI_D_ERROR,
    292       "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    293       mDaylightVariableName,
    294       Status
    295       ));
    296     goto EXIT;
    297   }
    298 
    299   EXIT:
    300   return Status;
    301 }
    302 
    303 
    304 /**
    305   Returns the current wakeup alarm clock setting.
    306 
    307   @param  Enabled               Indicates if the alarm is currently enabled or disabled.
    308   @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
    309   @param  Time                  The current alarm setting.
    310 
    311   @retval EFI_SUCCESS           The alarm settings were returned.
    312   @retval EFI_INVALID_PARAMETER Any parameter is NULL.
    313   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
    314 
    315 **/
    316 EFI_STATUS
    317 EFIAPI
    318 LibGetWakeupTime (
    319   OUT BOOLEAN     *Enabled,
    320   OUT BOOLEAN     *Pending,
    321   OUT EFI_TIME    *Time
    322   )
    323 {
    324   // Not a required feature
    325   return EFI_UNSUPPORTED;
    326 }
    327 
    328 
    329 /**
    330   Sets the system wakeup alarm clock time.
    331 
    332   @param  Enabled               Enable or disable the wakeup alarm.
    333   @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
    334 
    335   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
    336                                 Enable is FALSE, then the wakeup alarm was disabled.
    337   @retval EFI_INVALID_PARAMETER A time field is out of range.
    338   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
    339   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
    340 
    341 **/
    342 EFI_STATUS
    343 EFIAPI
    344 LibSetWakeupTime (
    345   IN BOOLEAN      Enabled,
    346   OUT EFI_TIME    *Time
    347   )
    348 {
    349   // Not a required feature
    350   return EFI_UNSUPPORTED;
    351 }
    352 
    353 /**
    354   Fixup internal data so that EFI can be call in virtual mode.
    355   Call the passed in Child Notify event and convert any pointers in
    356   lib to virtual mode.
    357 
    358   @param[in]    Event   The Event that is being processed
    359   @param[in]    Context Event Context
    360 **/
    361 VOID
    362 EFIAPI
    363 LibRtcVirtualNotifyEvent (
    364   IN EFI_EVENT        Event,
    365   IN VOID             *Context
    366   )
    367 {
    368   //
    369   // Only needed if you are going to support the OS calling RTC functions in virtual mode.
    370   // You will need to call EfiConvertPointer (). To convert any stored physical addresses
    371   // to virtual address. After the OS transitions to calling in virtual mode, all future
    372   // runtime calls will be made in virtual mode.
    373   //
    374   EfiConvertPointer (0x0, (VOID**)&mRT);
    375   return;
    376 }
    377 
    378 /**
    379   This is the declaration of an EFI image entry point. This can be the entry point to an application
    380   written to this specification, an EFI boot service driver, or an EFI runtime driver.
    381 
    382   @param  ImageHandle           Handle that identifies the loaded image.
    383   @param  SystemTable           System Table for this image.
    384 
    385   @retval EFI_SUCCESS           The operation completed successfully.
    386 
    387 **/
    388 EFI_STATUS
    389 EFIAPI
    390 LibRtcInitialize (
    391   IN EFI_HANDLE                            ImageHandle,
    392   IN EFI_SYSTEM_TABLE                      *SystemTable
    393   )
    394 {
    395   EFI_STATUS    Status;
    396   EFI_HANDLE    Handle;
    397 
    398   // Setup the setters and getters
    399   gRT->GetTime       = LibGetTime;
    400   gRT->SetTime       = LibSetTime;
    401   gRT->GetWakeupTime = LibGetWakeupTime;
    402   gRT->SetWakeupTime = LibSetWakeupTime;
    403 
    404   mRT = gRT;
    405 
    406   // Install the protocol
    407   Handle = NULL;
    408   Status = gBS->InstallMultipleProtocolInterfaces (
    409                   &Handle,
    410                   &gEfiRealTimeClockArchProtocolGuid,  NULL,
    411                   NULL
    412                  );
    413   ASSERT_EFI_ERROR (Status);
    414 
    415   //
    416   // Register for the virtual address change event
    417   //
    418   Status = gBS->CreateEventEx (
    419                   EVT_NOTIFY_SIGNAL,
    420                   TPL_NOTIFY,
    421                   LibRtcVirtualNotifyEvent,
    422                   NULL,
    423                   &gEfiEventVirtualAddressChangeGuid,
    424                   &mRtcVirtualAddrChangeEvent
    425                   );
    426   ASSERT_EFI_ERROR (Status);
    427 
    428   return Status;
    429 }
    430