Home | History | Annotate | Download | only in PL031RealTimeClockLib
      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 
      7   This program and the accompanying materials
      8   are licensed and made available under the terms and conditions of the BSD License
      9   which accompanies this distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include <Uefi.h>
     18 #include <PiDxe.h>
     19 #include <Library/BaseLib.h>
     20 #include <Library/DebugLib.h>
     21 #include <Library/UefiLib.h>
     22 #include <Library/IoLib.h>
     23 #include <Library/RealTimeClockLib.h>
     24 #include <Library/MemoryAllocationLib.h>
     25 #include <Library/PcdLib.h>
     26 #include <Library/ArmPlatformSysConfigLib.h>
     27 #include <Library/DxeServicesTableLib.h>
     28 #include <Library/UefiBootServicesTableLib.h>
     29 #include <Library/UefiRuntimeServicesTableLib.h>
     30 #include <Library/UefiRuntimeLib.h>
     31 
     32 #include <Protocol/RealTimeClock.h>
     33 
     34 #include <Guid/GlobalVariable.h>
     35 #include <Guid/EventGroup.h>
     36 
     37 #include <Drivers/PL031RealTimeClock.h>
     38 
     39 #include <ArmPlatform.h>
     40 
     41 STATIC CONST CHAR16           mTimeZoneVariableName[] = L"PL031RtcTimeZone";
     42 STATIC CONST CHAR16           mDaylightVariableName[] = L"PL031RtcDaylight";
     43 STATIC BOOLEAN                mPL031Initialized = FALSE;
     44 STATIC EFI_EVENT              mRtcVirtualAddrChangeEvent;
     45 STATIC UINTN                  mPL031RtcBase;
     46 
     47 EFI_STATUS
     48 IdentifyPL031 (
     49   VOID
     50   )
     51 {
     52   EFI_STATUS    Status;
     53 
     54   // Check if this is a PrimeCell Peripheral
     55   if (  (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID0) != 0x0D)
     56       || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID1) != 0xF0)
     57       || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID2) != 0x05)
     58       || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID3) != 0xB1)) {
     59     Status = EFI_NOT_FOUND;
     60     goto EXIT;
     61   }
     62 
     63   // Check if this PrimeCell Peripheral is the PL031 Real Time Clock
     64   if (  (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID0) != 0x31)
     65       || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID1) != 0x10)
     66       || ((MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID2) & 0xF) != 0x04)
     67       || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID3) != 0x00)) {
     68     Status = EFI_NOT_FOUND;
     69     goto EXIT;
     70   }
     71 
     72   Status = EFI_SUCCESS;
     73 
     74   EXIT:
     75   return Status;
     76 }
     77 
     78 EFI_STATUS
     79 InitializePL031 (
     80   VOID
     81   )
     82 {
     83   EFI_STATUS    Status;
     84 
     85   // Prepare the hardware
     86   Status = IdentifyPL031();
     87   if (EFI_ERROR (Status)) {
     88     goto EXIT;
     89   }
     90 
     91   // Ensure interrupts are masked. We do not want RTC interrupts in UEFI
     92   if ((MmioRead32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER) & PL031_SET_IRQ_MASK) != PL031_SET_IRQ_MASK) {
     93     MmioOr32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER, PL031_SET_IRQ_MASK);
     94   }
     95 
     96   // Clear any existing interrupts
     97   if ((MmioRead32 (mPL031RtcBase + PL031_RTC_RIS_RAW_IRQ_STATUS_REGISTER) & PL031_IRQ_TRIGGERED) == PL031_IRQ_TRIGGERED) {
     98     MmioOr32 (mPL031RtcBase + PL031_RTC_ICR_IRQ_CLEAR_REGISTER, PL031_CLEAR_IRQ);
     99   }
    100 
    101   // Start the clock counter
    102   if ((MmioRead32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER) & PL031_RTC_ENABLED) != PL031_RTC_ENABLED) {
    103     MmioOr32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER, PL031_RTC_ENABLED);
    104   }
    105 
    106   mPL031Initialized = TRUE;
    107 
    108   EXIT:
    109   return Status;
    110 }
    111 
    112 /**
    113   Converts Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC) to EFI_TIME
    114  **/
    115 VOID
    116 EpochToEfiTime (
    117   IN  UINTN     EpochSeconds,
    118   OUT EFI_TIME  *Time
    119   )
    120 {
    121   UINTN         a;
    122   UINTN         b;
    123   UINTN         c;
    124   UINTN         d;
    125   UINTN         g;
    126   UINTN         j;
    127   UINTN         m;
    128   UINTN         y;
    129   UINTN         da;
    130   UINTN         db;
    131   UINTN         dc;
    132   UINTN         dg;
    133   UINTN         hh;
    134   UINTN         mm;
    135   UINTN         ss;
    136   UINTN         J;
    137 
    138   J  = (EpochSeconds / 86400) + 2440588;
    139   j  = J + 32044;
    140   g  = j / 146097;
    141   dg = j % 146097;
    142   c  = (((dg / 36524) + 1) * 3) / 4;
    143   dc = dg - (c * 36524);
    144   b  = dc / 1461;
    145   db = dc % 1461;
    146   a  = (((db / 365) + 1) * 3) / 4;
    147   da = db - (a * 365);
    148   y  = (g * 400) + (c * 100) + (b * 4) + a;
    149   m  = (((da * 5) + 308) / 153) - 2;
    150   d  = da - (((m + 4) * 153) / 5) + 122;
    151 
    152   Time->Year  = y - 4800 + ((m + 2) / 12);
    153   Time->Month = ((m + 2) % 12) + 1;
    154   Time->Day   = d + 1;
    155 
    156   ss = EpochSeconds % 60;
    157   a  = (EpochSeconds - ss) / 60;
    158   mm = a % 60;
    159   b = (a - mm) / 60;
    160   hh = b % 24;
    161 
    162   Time->Hour        = hh;
    163   Time->Minute      = mm;
    164   Time->Second      = ss;
    165   Time->Nanosecond  = 0;
    166 
    167 }
    168 
    169 /**
    170   Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
    171  **/
    172 UINTN
    173 EfiTimeToEpoch (
    174   IN  EFI_TIME  *Time
    175   )
    176 {
    177   UINTN a;
    178   UINTN y;
    179   UINTN m;
    180   UINTN JulianDate;  // Absolute Julian Date representation of the supplied Time
    181   UINTN EpochDays;   // Number of days elapsed since EPOCH_JULIAN_DAY
    182   UINTN EpochSeconds;
    183 
    184   a = (14 - Time->Month) / 12 ;
    185   y = Time->Year + 4800 - a;
    186   m = Time->Month + (12*a) - 3;
    187 
    188   JulianDate = Time->Day + ((153*m + 2)/5) + (365*y) + (y/4) - (y/100) + (y/400) - 32045;
    189 
    190   ASSERT (JulianDate >= EPOCH_JULIAN_DATE);
    191   EpochDays = JulianDate - EPOCH_JULIAN_DATE;
    192 
    193   EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
    194 
    195   return EpochSeconds;
    196 }
    197 
    198 BOOLEAN
    199 IsLeapYear (
    200   IN EFI_TIME   *Time
    201   )
    202 {
    203   if (Time->Year % 4 == 0) {
    204     if (Time->Year % 100 == 0) {
    205       if (Time->Year % 400 == 0) {
    206         return TRUE;
    207       } else {
    208         return FALSE;
    209       }
    210     } else {
    211       return TRUE;
    212     }
    213   } else {
    214     return FALSE;
    215   }
    216 }
    217 
    218 BOOLEAN
    219 DayValid (
    220   IN  EFI_TIME  *Time
    221   )
    222 {
    223   STATIC CONST INTN DayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    224 
    225   if (Time->Day < 1 ||
    226       Time->Day > DayOfMonth[Time->Month - 1] ||
    227       (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
    228      ) {
    229     return FALSE;
    230   }
    231 
    232   return TRUE;
    233 }
    234 
    235 /**
    236   Returns the current time and date information, and the time-keeping capabilities
    237   of the hardware platform.
    238 
    239   @param  Time                   A pointer to storage to receive a snapshot of the current time.
    240   @param  Capabilities           An optional pointer to a buffer to receive the real time clock
    241                                  device's capabilities.
    242 
    243   @retval EFI_SUCCESS            The operation completed successfully.
    244   @retval EFI_INVALID_PARAMETER  Time is NULL.
    245   @retval EFI_DEVICE_ERROR       The time could not be retrieved due to hardware error.
    246   @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
    247 
    248 **/
    249 EFI_STATUS
    250 EFIAPI
    251 LibGetTime (
    252   OUT EFI_TIME                *Time,
    253   OUT EFI_TIME_CAPABILITIES   *Capabilities
    254   )
    255 {
    256   EFI_STATUS  Status = EFI_SUCCESS;
    257   UINT32      EpochSeconds;
    258   INT16       TimeZone;
    259   UINT8       Daylight;
    260   UINTN       Size;
    261 
    262   // Initialize the hardware if not already done
    263   if (!mPL031Initialized) {
    264     Status = InitializePL031 ();
    265     if (EFI_ERROR (Status)) {
    266       goto EXIT;
    267     }
    268   }
    269 
    270   // Snapshot the time as early in the function call as possible
    271   // On some platforms we may have access to a battery backed up hardware clock.
    272   // If such RTC exists try to use it first.
    273   Status = ArmPlatformSysConfigGet (SYS_CFG_RTC, &EpochSeconds);
    274   if (Status == EFI_UNSUPPORTED) {
    275     // Battery backed up hardware RTC does not exist, revert to PL031
    276     EpochSeconds = MmioRead32 (mPL031RtcBase + PL031_RTC_DR_DATA_REGISTER);
    277     Status = EFI_SUCCESS;
    278   } else if (EFI_ERROR (Status)) {
    279     // Battery backed up hardware RTC exists but could not be read due to error. Abort.
    280     goto EXIT;
    281   } else {
    282     // Battery backed up hardware RTC exists and we read the time correctly from it.
    283     // Now sync the PL031 to the new time.
    284     MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);
    285   }
    286 
    287   // Ensure Time is a valid pointer
    288   if (Time == NULL) {
    289     Status = EFI_INVALID_PARAMETER;
    290     goto EXIT;
    291   }
    292 
    293   // Get the current time zone information from non-volatile storage
    294   Size = sizeof (TimeZone);
    295   Status = EfiGetVariable (
    296                   (CHAR16 *)mTimeZoneVariableName,
    297                   &gEfiCallerIdGuid,
    298                   NULL,
    299                   &Size,
    300                   (VOID *)&TimeZone
    301                   );
    302 
    303   if (EFI_ERROR (Status)) {
    304     ASSERT(Status != EFI_INVALID_PARAMETER);
    305     ASSERT(Status != EFI_BUFFER_TOO_SMALL);
    306 
    307     if (Status != EFI_NOT_FOUND)
    308       goto EXIT;
    309 
    310     // The time zone variable does not exist in non-volatile storage, so create it.
    311     Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
    312     // Store it
    313     Status = EfiSetVariable (
    314                     (CHAR16 *)mTimeZoneVariableName,
    315                     &gEfiCallerIdGuid,
    316                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    317                     Size,
    318                     (VOID *)&(Time->TimeZone)
    319                     );
    320     if (EFI_ERROR (Status)) {
    321       DEBUG ((
    322         EFI_D_ERROR,
    323         "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    324         mTimeZoneVariableName,
    325         Status
    326         ));
    327       goto EXIT;
    328     }
    329   } else {
    330     // Got the time zone
    331     Time->TimeZone = TimeZone;
    332 
    333     // Check TimeZone bounds:   -1440 to 1440 or 2047
    334     if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440))
    335         && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {
    336       Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
    337     }
    338 
    339     // Adjust for the correct time zone
    340     if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
    341       EpochSeconds += Time->TimeZone * SEC_PER_MIN;
    342     }
    343   }
    344 
    345   // Get the current daylight information from non-volatile storage
    346   Size = sizeof (Daylight);
    347   Status = EfiGetVariable (
    348                   (CHAR16 *)mDaylightVariableName,
    349                   &gEfiCallerIdGuid,
    350                   NULL,
    351                   &Size,
    352                   (VOID *)&Daylight
    353                   );
    354 
    355   if (EFI_ERROR (Status)) {
    356     ASSERT(Status != EFI_INVALID_PARAMETER);
    357     ASSERT(Status != EFI_BUFFER_TOO_SMALL);
    358 
    359     if (Status != EFI_NOT_FOUND)
    360       goto EXIT;
    361 
    362     // The daylight variable does not exist in non-volatile storage, so create it.
    363     Time->Daylight = 0;
    364     // Store it
    365     Status = EfiSetVariable (
    366                     (CHAR16 *)mDaylightVariableName,
    367                     &gEfiCallerIdGuid,
    368                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    369                     Size,
    370                     (VOID *)&(Time->Daylight)
    371                     );
    372     if (EFI_ERROR (Status)) {
    373       DEBUG ((
    374         EFI_D_ERROR,
    375         "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    376         mDaylightVariableName,
    377         Status
    378         ));
    379       goto EXIT;
    380     }
    381   } else {
    382     // Got the daylight information
    383     Time->Daylight = Daylight;
    384 
    385     // Adjust for the correct period
    386     if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
    387       // Convert to adjusted time, i.e. spring forwards one hour
    388       EpochSeconds += SEC_PER_HOUR;
    389     }
    390   }
    391 
    392   // Convert from internal 32-bit time to UEFI time
    393   EpochToEfiTime (EpochSeconds, Time);
    394 
    395   // Update the Capabilities info
    396   if (Capabilities != NULL) {
    397     // PL031 runs at frequency 1Hz
    398     Capabilities->Resolution  = PL031_COUNTS_PER_SECOND;
    399     // Accuracy in ppm multiplied by 1,000,000, e.g. for 50ppm set 50,000,000
    400     Capabilities->Accuracy    = (UINT32)PcdGet32 (PcdPL031RtcPpmAccuracy);
    401     // FALSE: Setting the time does not clear the values below the resolution level
    402     Capabilities->SetsToZero  = FALSE;
    403   }
    404 
    405   EXIT:
    406   return Status;
    407 }
    408 
    409 
    410 /**
    411   Sets the current local time and date information.
    412 
    413   @param  Time                  A pointer to the current time.
    414 
    415   @retval EFI_SUCCESS           The operation completed successfully.
    416   @retval EFI_INVALID_PARAMETER A time field is out of range.
    417   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
    418 
    419 **/
    420 EFI_STATUS
    421 EFIAPI
    422 LibSetTime (
    423   IN  EFI_TIME                *Time
    424   )
    425 {
    426   EFI_STATUS  Status;
    427   UINTN       EpochSeconds;
    428 
    429   // Check the input parameters are within the range specified by UEFI
    430   if ((Time->Year   < 1900) ||
    431        (Time->Year   > 9999) ||
    432        (Time->Month  < 1   ) ||
    433        (Time->Month  > 12  ) ||
    434        (!DayValid (Time)    ) ||
    435        (Time->Hour   > 23  ) ||
    436        (Time->Minute > 59  ) ||
    437        (Time->Second > 59  ) ||
    438        (Time->Nanosecond > 999999999) ||
    439        (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||
    440        (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT)))
    441     ) {
    442     Status = EFI_INVALID_PARAMETER;
    443     goto EXIT;
    444   }
    445 
    446   // Because the PL031 is a 32-bit counter counting seconds,
    447   // the maximum time span is just over 136 years.
    448   // Time is stored in Unix Epoch format, so it starts in 1970,
    449   // Therefore it can not exceed the year 2106.
    450   if ((Time->Year < 1970) || (Time->Year >= 2106)) {
    451     Status = EFI_UNSUPPORTED;
    452     goto EXIT;
    453   }
    454 
    455   // Initialize the hardware if not already done
    456   if (!mPL031Initialized) {
    457     Status = InitializePL031 ();
    458     if (EFI_ERROR (Status)) {
    459       goto EXIT;
    460     }
    461   }
    462 
    463   EpochSeconds = EfiTimeToEpoch (Time);
    464 
    465   // Adjust for the correct time zone, i.e. convert to UTC time zone
    466   if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
    467     EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
    468   }
    469 
    470   // TODO: Automatic Daylight activation
    471 
    472   // Adjust for the correct period
    473   if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
    474     // Convert to un-adjusted time, i.e. fall back one hour
    475     EpochSeconds -= SEC_PER_HOUR;
    476   }
    477 
    478   // On some platforms we may have access to a battery backed up hardware clock.
    479   //
    480   // If such RTC exists then it must be updated first, before the PL031,
    481   // to minimise any time drift. This is important because the battery backed-up
    482   // RTC maintains the master time for the platform across reboots.
    483   //
    484   // If such RTC does not exist then the following function returns UNSUPPORTED.
    485   Status = ArmPlatformSysConfigSet (SYS_CFG_RTC, EpochSeconds);
    486   if ((EFI_ERROR (Status)) && (Status != EFI_UNSUPPORTED)){
    487     // Any status message except SUCCESS and UNSUPPORTED indicates a hardware failure.
    488     goto EXIT;
    489   }
    490 
    491 
    492   // Set the PL031
    493   MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);
    494 
    495   // The accesses to Variable Services can be very slow, because we may be writing to Flash.
    496   // Do this after having set the RTC.
    497 
    498   // Save the current time zone information into non-volatile storage
    499   Status = EfiSetVariable (
    500                   (CHAR16 *)mTimeZoneVariableName,
    501                   &gEfiCallerIdGuid,
    502                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    503                   sizeof (Time->TimeZone),
    504                   (VOID *)&(Time->TimeZone)
    505                   );
    506   if (EFI_ERROR (Status)) {
    507       DEBUG ((
    508         EFI_D_ERROR,
    509         "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    510         mTimeZoneVariableName,
    511         Status
    512         ));
    513     goto EXIT;
    514   }
    515 
    516   // Save the current daylight information into non-volatile storage
    517   Status = EfiSetVariable (
    518                   (CHAR16 *)mDaylightVariableName,
    519                   &gEfiCallerIdGuid,
    520                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    521                   sizeof(Time->Daylight),
    522                   (VOID *)&(Time->Daylight)
    523                   );
    524   if (EFI_ERROR (Status)) {
    525     DEBUG ((
    526       EFI_D_ERROR,
    527       "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
    528       mDaylightVariableName,
    529       Status
    530       ));
    531     goto EXIT;
    532   }
    533 
    534   EXIT:
    535   return Status;
    536 }
    537 
    538 
    539 /**
    540   Returns the current wakeup alarm clock setting.
    541 
    542   @param  Enabled               Indicates if the alarm is currently enabled or disabled.
    543   @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
    544   @param  Time                  The current alarm setting.
    545 
    546   @retval EFI_SUCCESS           The alarm settings were returned.
    547   @retval EFI_INVALID_PARAMETER Any parameter is NULL.
    548   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
    549 
    550 **/
    551 EFI_STATUS
    552 EFIAPI
    553 LibGetWakeupTime (
    554   OUT BOOLEAN     *Enabled,
    555   OUT BOOLEAN     *Pending,
    556   OUT EFI_TIME    *Time
    557   )
    558 {
    559   // Not a required feature
    560   return EFI_UNSUPPORTED;
    561 }
    562 
    563 
    564 /**
    565   Sets the system wakeup alarm clock time.
    566 
    567   @param  Enabled               Enable or disable the wakeup alarm.
    568   @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
    569 
    570   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
    571                                 Enable is FALSE, then the wakeup alarm was disabled.
    572   @retval EFI_INVALID_PARAMETER A time field is out of range.
    573   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
    574   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
    575 
    576 **/
    577 EFI_STATUS
    578 EFIAPI
    579 LibSetWakeupTime (
    580   IN BOOLEAN      Enabled,
    581   OUT EFI_TIME    *Time
    582   )
    583 {
    584   // Not a required feature
    585   return EFI_UNSUPPORTED;
    586 }
    587 
    588 /**
    589   Fixup internal data so that EFI can be call in virtual mode.
    590   Call the passed in Child Notify event and convert any pointers in
    591   lib to virtual mode.
    592 
    593   @param[in]    Event   The Event that is being processed
    594   @param[in]    Context Event Context
    595 **/
    596 VOID
    597 EFIAPI
    598 LibRtcVirtualNotifyEvent (
    599   IN EFI_EVENT        Event,
    600   IN VOID             *Context
    601   )
    602 {
    603   //
    604   // Only needed if you are going to support the OS calling RTC functions in virtual mode.
    605   // You will need to call EfiConvertPointer (). To convert any stored physical addresses
    606   // to virtual address. After the OS transitions to calling in virtual mode, all future
    607   // runtime calls will be made in virtual mode.
    608   //
    609   EfiConvertPointer (0x0, (VOID**)&mPL031RtcBase);
    610   return;
    611 }
    612 
    613 /**
    614   This is the declaration of an EFI image entry point. This can be the entry point to an application
    615   written to this specification, an EFI boot service driver, or an EFI runtime driver.
    616 
    617   @param  ImageHandle           Handle that identifies the loaded image.
    618   @param  SystemTable           System Table for this image.
    619 
    620   @retval EFI_SUCCESS           The operation completed successfully.
    621 
    622 **/
    623 EFI_STATUS
    624 EFIAPI
    625 LibRtcInitialize (
    626   IN EFI_HANDLE                            ImageHandle,
    627   IN EFI_SYSTEM_TABLE                      *SystemTable
    628   )
    629 {
    630   EFI_STATUS    Status;
    631   EFI_HANDLE    Handle;
    632 
    633   // Initialize RTC Base Address
    634   mPL031RtcBase = PcdGet32 (PcdPL031RtcBase);
    635 
    636   // Declare the controller as EFI_MEMORY_RUNTIME
    637   Status = gDS->AddMemorySpace (
    638                   EfiGcdMemoryTypeMemoryMappedIo,
    639                   mPL031RtcBase, SIZE_4KB,
    640                   EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
    641                   );
    642   if (EFI_ERROR (Status)) {
    643     return Status;
    644   }
    645 
    646   Status = gDS->SetMemorySpaceAttributes (mPL031RtcBase, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
    647   if (EFI_ERROR (Status)) {
    648     return Status;
    649   }
    650 
    651   // Install the protocol
    652   Handle = NULL;
    653   Status = gBS->InstallMultipleProtocolInterfaces (
    654                   &Handle,
    655                   &gEfiRealTimeClockArchProtocolGuid,  NULL,
    656                   NULL
    657                  );
    658   ASSERT_EFI_ERROR (Status);
    659 
    660   //
    661   // Register for the virtual address change event
    662   //
    663   Status = gBS->CreateEventEx (
    664                   EVT_NOTIFY_SIGNAL,
    665                   TPL_NOTIFY,
    666                   LibRtcVirtualNotifyEvent,
    667                   NULL,
    668                   &gEfiEventVirtualAddressChangeGuid,
    669                   &mRtcVirtualAddrChangeEvent
    670                   );
    671   ASSERT_EFI_ERROR (Status);
    672 
    673   return Status;
    674 }
    675