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