• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
IdentifyPL031(VOID)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
InitializePL031(VOID)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
EpochToEfiTime(IN UINTN EpochSeconds,OUT EFI_TIME * Time)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
EfiTimeToEpoch(IN EFI_TIME * Time)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
IsLeapYear(IN EFI_TIME * Time)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
DayValid(IN EFI_TIME * Time)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
LibGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)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
LibSetTime(IN EFI_TIME * Time)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
LibGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)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
LibSetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)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
LibRtcVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)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
LibRtcInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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