1 /** @file
2   RTC Architectural Protocol GUID as defined in DxeCis 0.96.
3 
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "PcRtc.h"
16 
17 /**
18   Compare the Hour, Minute and Second of the From time and the To time.
19 
20   Only compare H/M/S in EFI_TIME and ignore other fields here.
21 
22   @param From   the first time
23   @param To     the second time
24 
25   @return  >0   The H/M/S of the From time is later than those of To time
26   @return  ==0  The H/M/S of the From time is same as those of To time
27   @return  <0   The H/M/S of the From time is earlier than those of To time
28 **/
29 INTN
30 CompareHMS (
31   IN EFI_TIME   *From,
32   IN EFI_TIME   *To
33   );
34 
35 /**
36   To check if second date is later than first date within 24 hours.
37 
38   @param  From   the first date
39   @param  To     the second date
40 
41   @retval TRUE   From is previous to To within 24 hours.
42   @retval FALSE  From is later, or it is previous to To more than 24 hours.
43 **/
44 BOOLEAN
45 IsWithinOneDay (
46   IN EFI_TIME   *From,
47   IN EFI_TIME   *To
48   );
49 
50 /**
51   Read RTC content through its registers.
52 
53   @param  Address  Address offset of RTC. It is recommended to use macros such as
54                    RTC_ADDRESS_SECONDS.
55 
56   @return The data of UINT8 type read from RTC.
57 **/
58 UINT8
RtcRead(IN UINT8 Address)59 RtcRead (
60   IN  UINT8 Address
61   )
62 {
63   IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
64   return IoRead8 (PCAT_RTC_DATA_REGISTER);
65 }
66 
67 /**
68   Write RTC through its registers.
69 
70   @param  Address  Address offset of RTC. It is recommended to use macros such as
71                    RTC_ADDRESS_SECONDS.
72   @param  Data     The content you want to write into RTC.
73 
74 **/
75 VOID
RtcWrite(IN UINT8 Address,IN UINT8 Data)76 RtcWrite (
77   IN  UINT8   Address,
78   IN  UINT8   Data
79   )
80 {
81   IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
82   IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);
83 }
84 
85 /**
86   Initialize RTC.
87 
88   @param  Global            For global use inside this module.
89 
90   @retval EFI_DEVICE_ERROR  Initialization failed due to device error.
91   @retval EFI_SUCCESS       Initialization successful.
92 
93 **/
94 EFI_STATUS
PcRtcInit(IN PC_RTC_MODULE_GLOBALS * Global)95 PcRtcInit (
96   IN PC_RTC_MODULE_GLOBALS  *Global
97   )
98 {
99   EFI_STATUS      Status;
100   RTC_REGISTER_A  RegisterA;
101   RTC_REGISTER_B  RegisterB;
102   RTC_REGISTER_D  RegisterD;
103   EFI_TIME        Time;
104   UINTN           DataSize;
105   UINT32          TimerVar;
106   BOOLEAN         Enabled;
107   BOOLEAN         Pending;
108 
109   //
110   // Acquire RTC Lock to make access to RTC atomic
111   //
112   if (!EfiAtRuntime ()) {
113     EfiAcquireLock (&Global->RtcLock);
114   }
115   //
116   // Initialize RTC Register
117   //
118   // Make sure Division Chain is properly configured,
119   // or RTC clock won't "tick" -- time won't increment
120   //
121   RegisterA.Data = RTC_INIT_REGISTER_A;
122   RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
123 
124   //
125   // Read Register B
126   //
127   RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
128 
129   //
130   // Clear RTC flag register
131   //
132   RtcRead (RTC_ADDRESS_REGISTER_C);
133 
134   //
135   // Clear RTC register D
136   //
137   RegisterD.Data = RTC_INIT_REGISTER_D;
138   RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
139 
140   //
141   // Wait for up to 0.1 seconds for the RTC to be updated
142   //
143   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
144   if (EFI_ERROR (Status)) {
145     //
146     // Set the variable with default value if the RTC is functioning incorrectly.
147     //
148     Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;
149     Global->Daylight      = 0;
150     if (!EfiAtRuntime ()) {
151       EfiReleaseLock (&Global->RtcLock);
152     }
153     return EFI_DEVICE_ERROR;
154   }
155   //
156   // Get the Time/Date/Daylight Savings values.
157   //
158   Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
159   Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
160   Time.Hour   = RtcRead (RTC_ADDRESS_HOURS);
161   Time.Day    = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
162   Time.Month  = RtcRead (RTC_ADDRESS_MONTH);
163   Time.Year   = RtcRead (RTC_ADDRESS_YEAR);
164 
165   //
166   // Set RTC configuration after get original time
167   // The value of bit AIE should be reserved.
168   //
169   RegisterB.Data = RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5);
170   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
171 
172   //
173   // Release RTC Lock.
174   //
175   if (!EfiAtRuntime ()) {
176     EfiReleaseLock (&Global->RtcLock);
177   }
178 
179   //
180   // Get the data of Daylight saving and time zone, if they have been
181   // stored in NV variable during previous boot.
182   //
183   DataSize = sizeof (UINT32);
184   Status = EfiGetVariable (
185              L"RTC",
186              &gEfiCallerIdGuid,
187              NULL,
188              &DataSize,
189              (VOID *) &TimerVar
190              );
191   if (!EFI_ERROR (Status)) {
192     Time.TimeZone = (INT16) TimerVar;
193     Time.Daylight = (UINT8) (TimerVar >> 16);
194   } else {
195     Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
196     Time.Daylight = 0;
197   }
198 
199   //
200   // Validate time fields
201   //
202   Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);
203   if (!EFI_ERROR (Status)) {
204     Status = RtcTimeFieldsValid (&Time);
205   }
206   if (EFI_ERROR (Status)) {
207     //
208     // Report Status Code to indicate that the RTC has bad date and time
209     //
210     REPORT_STATUS_CODE (
211       EFI_ERROR_CODE | EFI_ERROR_MINOR,
212       (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)
213       );
214     Time.Second = RTC_INIT_SECOND;
215     Time.Minute = RTC_INIT_MINUTE;
216     Time.Hour   = RTC_INIT_HOUR;
217     Time.Day    = RTC_INIT_DAY;
218     Time.Month  = RTC_INIT_MONTH;
219     Time.Year   = PcdGet16 (PcdMinimalValidYear);
220     Time.Nanosecond  = 0;
221     Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
222     Time.Daylight = 0;
223   }
224 
225   //
226   // Reset time value according to new RTC configuration
227   //
228   Status = PcRtcSetTime (&Time, Global);
229   if (EFI_ERROR (Status)) {
230     return EFI_DEVICE_ERROR;
231   }
232 
233   //
234   // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
235   // Global variable has already had valid SavedTimeZone and Daylight,
236   // so we can use them to get and set wakeup time.
237   //
238   Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);
239   if ((Enabled) || (!EFI_ERROR (Status))) {
240     return EFI_SUCCESS;
241   }
242 
243   //
244   // When wakeup time is disabled and invalid, reset wakeup time register to valid state
245   // but keep wakeup alarm disabled.
246   //
247   Time.Second = RTC_INIT_SECOND;
248   Time.Minute = RTC_INIT_MINUTE;
249   Time.Hour   = RTC_INIT_HOUR;
250   Time.Day    = RTC_INIT_DAY;
251   Time.Month  = RTC_INIT_MONTH;
252   Time.Year   = PcdGet16 (PcdMinimalValidYear);
253   Time.Nanosecond  = 0;
254   Time.TimeZone = Global->SavedTimeZone;
255   Time.Daylight = Global->Daylight;;
256 
257   //
258   // Acquire RTC Lock to make access to RTC atomic
259   //
260   if (!EfiAtRuntime ()) {
261     EfiAcquireLock (&Global->RtcLock);
262   }
263   //
264   // Wait for up to 0.1 seconds for the RTC to be updated
265   //
266   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
267   if (EFI_ERROR (Status)) {
268     if (!EfiAtRuntime ()) {
269     EfiReleaseLock (&Global->RtcLock);
270     }
271     return EFI_DEVICE_ERROR;
272   }
273 
274   ConvertEfiTimeToRtcTime (&Time, RegisterB);
275 
276   //
277   // Set the Y/M/D info to variable as it has no corresponding hw registers.
278   //
279   Status =  EfiSetVariable (
280               L"RTCALARM",
281               &gEfiCallerIdGuid,
282               EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
283               sizeof (Time),
284               &Time
285               );
286   if (EFI_ERROR (Status)) {
287     if (!EfiAtRuntime ()) {
288       EfiReleaseLock (&Global->RtcLock);
289     }
290     return EFI_DEVICE_ERROR;
291   }
292 
293   //
294   // Inhibit updates of the RTC
295   //
296   RegisterB.Bits.Set  = 1;
297   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
298 
299   //
300   // Set RTC alarm time registers
301   //
302   RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);
303   RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);
304   RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);
305 
306   //
307   // Allow updates of the RTC registers
308   //
309   RegisterB.Bits.Set = 0;
310   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
311 
312   //
313   // Release RTC Lock.
314   //
315   if (!EfiAtRuntime ()) {
316     EfiReleaseLock (&Global->RtcLock);
317   }
318   return EFI_SUCCESS;
319 }
320 
321 /**
322   Returns the current time and date information, and the time-keeping capabilities
323   of the hardware platform.
324 
325   @param  Time          A pointer to storage to receive a snapshot of the current time.
326   @param  Capabilities  An optional pointer to a buffer to receive the real time clock
327                         device's capabilities.
328   @param  Global        For global use inside this module.
329 
330   @retval EFI_SUCCESS            The operation completed successfully.
331   @retval EFI_INVALID_PARAMETER  Time is NULL.
332   @retval EFI_DEVICE_ERROR       The time could not be retrieved due to hardware error.
333 
334 **/
335 EFI_STATUS
PcRtcGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities,OPTIONAL IN PC_RTC_MODULE_GLOBALS * Global)336 PcRtcGetTime (
337   OUT  EFI_TIME               *Time,
338   OUT  EFI_TIME_CAPABILITIES  *Capabilities,  OPTIONAL
339   IN   PC_RTC_MODULE_GLOBALS  *Global
340   )
341 {
342   EFI_STATUS      Status;
343   RTC_REGISTER_B  RegisterB;
344 
345   //
346   // Check parameters for null pointer
347   //
348   if (Time == NULL) {
349     return EFI_INVALID_PARAMETER;
350 
351   }
352   //
353   // Acquire RTC Lock to make access to RTC atomic
354   //
355   if (!EfiAtRuntime ()) {
356     EfiAcquireLock (&Global->RtcLock);
357   }
358   //
359   // Wait for up to 0.1 seconds for the RTC to be updated
360   //
361   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
362   if (EFI_ERROR (Status)) {
363       if (!EfiAtRuntime ()) {
364         EfiReleaseLock (&Global->RtcLock);
365       }
366     return Status;
367   }
368   //
369   // Read Register B
370   //
371   RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
372 
373   //
374   // Get the Time/Date/Daylight Savings values.
375   //
376   Time->Second  = RtcRead (RTC_ADDRESS_SECONDS);
377   Time->Minute  = RtcRead (RTC_ADDRESS_MINUTES);
378   Time->Hour    = RtcRead (RTC_ADDRESS_HOURS);
379   Time->Day     = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
380   Time->Month   = RtcRead (RTC_ADDRESS_MONTH);
381   Time->Year    = RtcRead (RTC_ADDRESS_YEAR);
382 
383   //
384   // Release RTC Lock.
385   //
386   if (!EfiAtRuntime ()) {
387     EfiReleaseLock (&Global->RtcLock);
388   }
389 
390   //
391   // Get the variable that contains the TimeZone and Daylight fields
392   //
393   Time->TimeZone  = Global->SavedTimeZone;
394   Time->Daylight  = Global->Daylight;
395 
396   //
397   // Make sure all field values are in correct range
398   //
399   Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
400   if (!EFI_ERROR (Status)) {
401     Status = RtcTimeFieldsValid (Time);
402   }
403   if (EFI_ERROR (Status)) {
404     return EFI_DEVICE_ERROR;
405   }
406 
407   //
408   //  Fill in Capabilities if it was passed in
409   //
410   if (Capabilities != NULL) {
411     Capabilities->Resolution = 1;
412     //
413     // 1 hertz
414     //
415     Capabilities->Accuracy = 50000000;
416     //
417     // 50 ppm
418     //
419     Capabilities->SetsToZero = FALSE;
420   }
421 
422   return EFI_SUCCESS;
423 }
424 
425 /**
426   Sets the current local time and date information.
427 
428   @param  Time                  A pointer to the current time.
429   @param  Global                For global use inside this module.
430 
431   @retval EFI_SUCCESS           The operation completed successfully.
432   @retval EFI_INVALID_PARAMETER A time field is out of range.
433   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
434 
435 **/
436 EFI_STATUS
PcRtcSetTime(IN EFI_TIME * Time,IN PC_RTC_MODULE_GLOBALS * Global)437 PcRtcSetTime (
438   IN EFI_TIME                *Time,
439   IN PC_RTC_MODULE_GLOBALS   *Global
440   )
441 {
442   EFI_STATUS      Status;
443   EFI_TIME        RtcTime;
444   RTC_REGISTER_B  RegisterB;
445   UINT32          TimerVar;
446 
447   if (Time == NULL) {
448     return EFI_INVALID_PARAMETER;
449   }
450   //
451   // Make sure that the time fields are valid
452   //
453   Status = RtcTimeFieldsValid (Time);
454   if (EFI_ERROR (Status)) {
455     return Status;
456   }
457 
458   CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
459 
460   //
461   // Acquire RTC Lock to make access to RTC atomic
462   //
463   if (!EfiAtRuntime ()) {
464     EfiAcquireLock (&Global->RtcLock);
465   }
466   //
467   // Wait for up to 0.1 seconds for the RTC to be updated
468   //
469   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
470   if (EFI_ERROR (Status)) {
471      if (!EfiAtRuntime ()) {
472        EfiReleaseLock (&Global->RtcLock);
473      }
474     return Status;
475   }
476 
477   //
478   // Write timezone and daylight to RTC variable
479   //
480   TimerVar = Time->Daylight;
481   TimerVar = (UINT32) ((TimerVar << 16) | (UINT16)(Time->TimeZone));
482   Status =  EfiSetVariable (
483               L"RTC",
484               &gEfiCallerIdGuid,
485               EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
486               sizeof (TimerVar),
487               &TimerVar
488               );
489   if (EFI_ERROR (Status)) {
490     if (!EfiAtRuntime ()) {
491       EfiReleaseLock (&Global->RtcLock);
492     }
493     return EFI_DEVICE_ERROR;
494   }
495 
496   //
497   // Read Register B, and inhibit updates of the RTC
498   //
499   RegisterB.Data      = RtcRead (RTC_ADDRESS_REGISTER_B);
500   RegisterB.Bits.Set  = 1;
501   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
502 
503   //
504   // Store the century value to RTC before converting to BCD format.
505   //
506   if (Global->CenturyRtcAddress != 0) {
507     RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8) (RtcTime.Year / 100)));
508   }
509 
510   ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
511 
512   RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
513   RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
514   RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
515   RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
516   RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
517   RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);
518 
519   //
520   // Allow updates of the RTC registers
521   //
522   RegisterB.Bits.Set = 0;
523   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
524 
525   //
526   // Release RTC Lock.
527   //
528   if (!EfiAtRuntime ()) {
529     EfiReleaseLock (&Global->RtcLock);
530   }
531   //
532   // Set the variable that contains the TimeZone and Daylight fields
533   //
534   Global->SavedTimeZone = Time->TimeZone;
535   Global->Daylight      = Time->Daylight;
536 
537   return EFI_SUCCESS;
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 acknowledgment.
545   @param  Time     The current alarm setting.
546   @param  Global   For global use inside this module.
547 
548   @retval EFI_SUCCESS           The alarm settings were returned.
549   @retval EFI_INVALID_PARAMETER Enabled is NULL.
550   @retval EFI_INVALID_PARAMETER Pending is NULL.
551   @retval EFI_INVALID_PARAMETER Time is NULL.
552   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
553   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
554 
555 **/
556 EFI_STATUS
PcRtcGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time,IN PC_RTC_MODULE_GLOBALS * Global)557 PcRtcGetWakeupTime (
558   OUT BOOLEAN                *Enabled,
559   OUT BOOLEAN                *Pending,
560   OUT EFI_TIME               *Time,
561   IN  PC_RTC_MODULE_GLOBALS  *Global
562   )
563 {
564   EFI_STATUS      Status;
565   RTC_REGISTER_B  RegisterB;
566   RTC_REGISTER_C  RegisterC;
567   EFI_TIME        RtcTime;
568   UINTN           DataSize;
569 
570   //
571   // Check parameters for null pointers
572   //
573   if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
574     return EFI_INVALID_PARAMETER;
575 
576   }
577   //
578   // Acquire RTC Lock to make access to RTC atomic
579   //
580   if (!EfiAtRuntime ()) {
581     EfiAcquireLock (&Global->RtcLock);
582   }
583   //
584   // Wait for up to 0.1 seconds for the RTC to be updated
585   //
586   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
587   if (EFI_ERROR (Status)) {
588     if (!EfiAtRuntime ()) {
589     EfiReleaseLock (&Global->RtcLock);
590     }
591     return EFI_DEVICE_ERROR;
592   }
593   //
594   // Read Register B and Register C
595   //
596   RegisterB.Data  = RtcRead (RTC_ADDRESS_REGISTER_B);
597   RegisterC.Data  = RtcRead (RTC_ADDRESS_REGISTER_C);
598 
599   //
600   // Get the Time/Date/Daylight Savings values.
601   //
602   *Enabled = RegisterB.Bits.Aie;
603   *Pending = RegisterC.Bits.Af;
604 
605   Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
606   Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
607   Time->Hour   = RtcRead (RTC_ADDRESS_HOURS_ALARM);
608   Time->Day    = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
609   Time->Month  = RtcRead (RTC_ADDRESS_MONTH);
610   Time->Year   = RtcRead (RTC_ADDRESS_YEAR);
611   Time->TimeZone = Global->SavedTimeZone;
612   Time->Daylight = Global->Daylight;
613 
614   //
615   // Get the alarm info from variable
616   //
617   DataSize = sizeof (EFI_TIME);
618   Status = EfiGetVariable (
619               L"RTCALARM",
620               &gEfiCallerIdGuid,
621               NULL,
622               &DataSize,
623               &RtcTime
624               );
625   if (!EFI_ERROR (Status)) {
626     //
627     // The alarm variable exists. In this case, we read variable to get info.
628     //
629     Time->Day   = RtcTime.Day;
630     Time->Month = RtcTime.Month;
631     Time->Year  = RtcTime.Year;
632   }
633 
634   //
635   // Release RTC Lock.
636   //
637   if (!EfiAtRuntime ()) {
638     EfiReleaseLock (&Global->RtcLock);
639   }
640 
641   //
642   // Make sure all field values are in correct range
643   //
644   Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
645   if (!EFI_ERROR (Status)) {
646     Status = RtcTimeFieldsValid (Time);
647   }
648   if (EFI_ERROR (Status)) {
649     return EFI_DEVICE_ERROR;
650   }
651 
652   return EFI_SUCCESS;
653 }
654 
655 /**
656   Sets the system wakeup alarm clock time.
657 
658   @param  Enabled  Enable or disable the wakeup alarm.
659   @param  Time     If Enable is TRUE, the time to set the wakeup alarm for.
660                    If Enable is FALSE, then this parameter is optional, and may be NULL.
661   @param  Global   For global use inside this module.
662 
663   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled.
664                                 If Enable is FALSE, then the wakeup alarm was disabled.
665   @retval EFI_INVALID_PARAMETER A time field is out of range.
666   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
667   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
668 
669 **/
670 EFI_STATUS
PcRtcSetWakeupTime(IN BOOLEAN Enable,IN EFI_TIME * Time,OPTIONAL IN PC_RTC_MODULE_GLOBALS * Global)671 PcRtcSetWakeupTime (
672   IN BOOLEAN                Enable,
673   IN EFI_TIME               *Time,   OPTIONAL
674   IN PC_RTC_MODULE_GLOBALS  *Global
675   )
676 {
677   EFI_STATUS            Status;
678   EFI_TIME              RtcTime;
679   RTC_REGISTER_B        RegisterB;
680   EFI_TIME_CAPABILITIES Capabilities;
681 
682   ZeroMem (&RtcTime, sizeof (RtcTime));
683 
684   if (Enable) {
685 
686     if (Time == NULL) {
687       return EFI_INVALID_PARAMETER;
688     }
689     //
690     // Make sure that the time fields are valid
691     //
692     Status = RtcTimeFieldsValid (Time);
693     if (EFI_ERROR (Status)) {
694       return EFI_INVALID_PARAMETER;
695     }
696     //
697     // Just support set alarm time within 24 hours
698     //
699     PcRtcGetTime (&RtcTime, &Capabilities, Global);
700     Status = RtcTimeFieldsValid (&RtcTime);
701     if (EFI_ERROR (Status)) {
702       return EFI_DEVICE_ERROR;
703     }
704     if (!IsWithinOneDay (&RtcTime, Time)) {
705       return EFI_UNSUPPORTED;
706     }
707     //
708     // Make a local copy of the time and date
709     //
710     CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
711 
712   }
713   //
714   // Acquire RTC Lock to make access to RTC atomic
715   //
716   if (!EfiAtRuntime ()) {
717     EfiAcquireLock (&Global->RtcLock);
718   }
719   //
720   // Wait for up to 0.1 seconds for the RTC to be updated
721   //
722   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
723   if (EFI_ERROR (Status)) {
724     if (!EfiAtRuntime ()) {
725     EfiReleaseLock (&Global->RtcLock);
726     }
727     return EFI_DEVICE_ERROR;
728   }
729   //
730   // Read Register B
731   //
732   RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
733 
734   if (Enable) {
735     ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
736   } else {
737     //
738     // if the alarm is disable, record the current setting.
739     //
740     RtcTime.Second  = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
741     RtcTime.Minute  = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
742     RtcTime.Hour    = RtcRead (RTC_ADDRESS_HOURS_ALARM);
743     RtcTime.Day     = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
744     RtcTime.Month   = RtcRead (RTC_ADDRESS_MONTH);
745     RtcTime.Year    = RtcRead (RTC_ADDRESS_YEAR);
746     RtcTime.TimeZone = Global->SavedTimeZone;
747     RtcTime.Daylight = Global->Daylight;
748   }
749 
750   //
751   // Set the Y/M/D info to variable as it has no corresponding hw registers.
752   //
753   Status =  EfiSetVariable (
754               L"RTCALARM",
755               &gEfiCallerIdGuid,
756               EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
757               sizeof (RtcTime),
758               &RtcTime
759               );
760   if (EFI_ERROR (Status)) {
761     if (!EfiAtRuntime ()) {
762       EfiReleaseLock (&Global->RtcLock);
763     }
764     return EFI_DEVICE_ERROR;
765   }
766 
767   //
768   // Inhibit updates of the RTC
769   //
770   RegisterB.Bits.Set  = 1;
771   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
772 
773   if (Enable) {
774     //
775     // Set RTC alarm time
776     //
777     RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
778     RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
779     RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
780 
781     RegisterB.Bits.Aie = 1;
782 
783   } else {
784     RegisterB.Bits.Aie = 0;
785   }
786   //
787   // Allow updates of the RTC registers
788   //
789   RegisterB.Bits.Set = 0;
790   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
791 
792   //
793   // Release RTC Lock.
794   //
795   if (!EfiAtRuntime ()) {
796     EfiReleaseLock (&Global->RtcLock);
797   }
798   return EFI_SUCCESS;
799 }
800 
801 
802 /**
803   Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
804 
805   This function checks the 8-bit BCD value specified by Value.
806   If valid, the function converts it to an 8-bit value and returns it.
807   Otherwise, return 0xff.
808 
809   @param   Value The 8-bit BCD value to check and convert
810 
811   @return  The 8-bit value converted. Or 0xff if Value is invalid.
812 
813 **/
814 UINT8
CheckAndConvertBcd8ToDecimal8(IN UINT8 Value)815 CheckAndConvertBcd8ToDecimal8 (
816   IN  UINT8  Value
817   )
818 {
819   if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
820     return BcdToDecimal8 (Value);
821   }
822 
823   return 0xff;
824 }
825 
826 /**
827   Converts time read from RTC to EFI_TIME format defined by UEFI spec.
828 
829   This function converts raw time data read from RTC to the EFI_TIME format
830   defined by UEFI spec.
831   If data mode of RTC is BCD, then converts it to decimal,
832   If RTC is in 12-hour format, then converts it to 24-hour format.
833 
834   @param   Time       On input, the time data read from RTC to convert
835                       On output, the time converted to UEFI format
836   @param   RegisterB  Value of Register B of RTC, indicating data mode
837                       and hour format.
838 
839   @retval  EFI_INVALID_PARAMETER  Parameters passed in are invalid.
840   @retval  EFI_SUCCESS            Convert RTC time to EFI time successfully.
841 
842 **/
843 EFI_STATUS
ConvertRtcTimeToEfiTime(IN OUT EFI_TIME * Time,IN RTC_REGISTER_B RegisterB)844 ConvertRtcTimeToEfiTime (
845   IN OUT EFI_TIME        *Time,
846   IN     RTC_REGISTER_B  RegisterB
847   )
848 {
849   BOOLEAN IsPM;
850   UINT8   Century;
851 
852   if ((Time->Hour & 0x80) != 0) {
853     IsPM = TRUE;
854   } else {
855     IsPM = FALSE;
856   }
857 
858   Time->Hour = (UINT8) (Time->Hour & 0x7f);
859 
860   if (RegisterB.Bits.Dm == 0) {
861     Time->Year    = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);
862     Time->Month   = CheckAndConvertBcd8ToDecimal8 (Time->Month);
863     Time->Day     = CheckAndConvertBcd8ToDecimal8 (Time->Day);
864     Time->Hour    = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
865     Time->Minute  = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
866     Time->Second  = CheckAndConvertBcd8ToDecimal8 (Time->Second);
867   }
868 
869   if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||
870       Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff) {
871     return EFI_INVALID_PARAMETER;
872   }
873 
874   //
875   // For minimal/maximum year range [1970, 2069],
876   //   Century is 19 if RTC year >= 70,
877   //   Century is 20 otherwise.
878   //
879   Century = (UINT8) (PcdGet16 (PcdMinimalValidYear) / 100);
880   if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {
881     Century++;
882   }
883   Time->Year = (UINT16) (Century * 100 + Time->Year);
884 
885   //
886   // If time is in 12 hour format, convert it to 24 hour format
887   //
888   if (RegisterB.Bits.Mil == 0) {
889     if (IsPM && Time->Hour < 12) {
890       Time->Hour = (UINT8) (Time->Hour + 12);
891     }
892 
893     if (!IsPM && Time->Hour == 12) {
894       Time->Hour = 0;
895     }
896   }
897 
898   Time->Nanosecond  = 0;
899 
900   return EFI_SUCCESS;
901 }
902 
903 /**
904   Wait for a period for the RTC to be ready.
905 
906   @param    Timeout  Tell how long it should take to wait.
907 
908   @retval   EFI_DEVICE_ERROR   RTC device error.
909   @retval   EFI_SUCCESS        RTC is updated and ready.
910 **/
911 EFI_STATUS
RtcWaitToUpdate(UINTN Timeout)912 RtcWaitToUpdate (
913   UINTN Timeout
914   )
915 {
916   RTC_REGISTER_A  RegisterA;
917   RTC_REGISTER_D  RegisterD;
918 
919   //
920   // See if the RTC is functioning correctly
921   //
922   RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
923 
924   if (RegisterD.Bits.Vrt == 0) {
925     return EFI_DEVICE_ERROR;
926   }
927   //
928   // Wait for up to 0.1 seconds for the RTC to be ready.
929   //
930   Timeout         = (Timeout / 10) + 1;
931   RegisterA.Data  = RtcRead (RTC_ADDRESS_REGISTER_A);
932   while (RegisterA.Bits.Uip == 1 && Timeout > 0) {
933     MicroSecondDelay (10);
934     RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
935     Timeout--;
936   }
937 
938   RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
939   if (Timeout == 0 || RegisterD.Bits.Vrt == 0) {
940     return EFI_DEVICE_ERROR;
941   }
942 
943   return EFI_SUCCESS;
944 }
945 
946 /**
947   See if all fields of a variable of EFI_TIME type is correct.
948 
949   @param   Time   The time to be checked.
950 
951   @retval  EFI_INVALID_PARAMETER  Some fields of Time are not correct.
952   @retval  EFI_SUCCESS            Time is a valid EFI_TIME variable.
953 
954 **/
955 EFI_STATUS
RtcTimeFieldsValid(IN EFI_TIME * Time)956 RtcTimeFieldsValid (
957   IN EFI_TIME *Time
958   )
959 {
960   if (Time->Year < PcdGet16 (PcdMinimalValidYear) ||
961       Time->Year > PcdGet16 (PcdMaximalValidYear) ||
962       Time->Month < 1 ||
963       Time->Month > 12 ||
964       (!DayValid (Time)) ||
965       Time->Hour > 23 ||
966       Time->Minute > 59 ||
967       Time->Second > 59 ||
968       Time->Nanosecond > 999999999 ||
969       (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
970       ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {
971     return EFI_INVALID_PARAMETER;
972   }
973 
974   return EFI_SUCCESS;
975 }
976 
977 /**
978   See if field Day of an EFI_TIME is correct.
979 
980   @param    Time   Its Day field is to be checked.
981 
982   @retval   TRUE   Day field of Time is correct.
983   @retval   FALSE  Day field of Time is NOT correct.
984 **/
985 BOOLEAN
DayValid(IN EFI_TIME * Time)986 DayValid (
987   IN  EFI_TIME  *Time
988   )
989 {
990   INTN  DayOfMonth[12];
991 
992   DayOfMonth[0] = 31;
993   DayOfMonth[1] = 29;
994   DayOfMonth[2] = 31;
995   DayOfMonth[3] = 30;
996   DayOfMonth[4] = 31;
997   DayOfMonth[5] = 30;
998   DayOfMonth[6] = 31;
999   DayOfMonth[7] = 31;
1000   DayOfMonth[8] = 30;
1001   DayOfMonth[9] = 31;
1002   DayOfMonth[10] = 30;
1003   DayOfMonth[11] = 31;
1004 
1005   //
1006   // The validity of Time->Month field should be checked before
1007   //
1008   ASSERT (Time->Month >=1);
1009   ASSERT (Time->Month <=12);
1010   if (Time->Day < 1 ||
1011       Time->Day > DayOfMonth[Time->Month - 1] ||
1012       (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
1013       ) {
1014     return FALSE;
1015   }
1016 
1017   return TRUE;
1018 }
1019 
1020 /**
1021   Check if it is a leap year.
1022 
1023   @param    Time   The time to be checked.
1024 
1025   @retval   TRUE   It is a leap year.
1026   @retval   FALSE  It is NOT a leap year.
1027 **/
1028 BOOLEAN
IsLeapYear(IN EFI_TIME * Time)1029 IsLeapYear (
1030   IN EFI_TIME   *Time
1031   )
1032 {
1033   if (Time->Year % 4 == 0) {
1034     if (Time->Year % 100 == 0) {
1035       if (Time->Year % 400 == 0) {
1036         return TRUE;
1037       } else {
1038         return FALSE;
1039       }
1040     } else {
1041       return TRUE;
1042     }
1043   } else {
1044     return FALSE;
1045   }
1046 }
1047 
1048 /**
1049   Converts time from EFI_TIME format defined by UEFI spec to RTC's.
1050 
1051   This function converts time from EFI_TIME format defined by UEFI spec to RTC's.
1052   If data mode of RTC is BCD, then converts EFI_TIME to it.
1053   If RTC is in 12-hour format, then converts EFI_TIME to it.
1054 
1055   @param   Time       On input, the time data read from UEFI to convert
1056                       On output, the time converted to RTC format
1057   @param   RegisterB  Value of Register B of RTC, indicating data mode
1058 **/
1059 VOID
ConvertEfiTimeToRtcTime(IN OUT EFI_TIME * Time,IN RTC_REGISTER_B RegisterB)1060 ConvertEfiTimeToRtcTime (
1061   IN OUT EFI_TIME        *Time,
1062   IN     RTC_REGISTER_B  RegisterB
1063   )
1064 {
1065   BOOLEAN IsPM;
1066 
1067   IsPM = TRUE;
1068   //
1069   // Adjust hour field if RTC is in 12 hour mode
1070   //
1071   if (RegisterB.Bits.Mil == 0) {
1072     if (Time->Hour < 12) {
1073       IsPM = FALSE;
1074     }
1075 
1076     if (Time->Hour >= 13) {
1077       Time->Hour = (UINT8) (Time->Hour - 12);
1078     } else if (Time->Hour == 0) {
1079       Time->Hour = 12;
1080     }
1081   }
1082   //
1083   // Set the Time/Date values.
1084   //
1085   Time->Year  = (UINT16) (Time->Year % 100);
1086 
1087   if (RegisterB.Bits.Dm == 0) {
1088     Time->Year    = DecimalToBcd8 ((UINT8) Time->Year);
1089     Time->Month   = DecimalToBcd8 (Time->Month);
1090     Time->Day     = DecimalToBcd8 (Time->Day);
1091     Time->Hour    = DecimalToBcd8 (Time->Hour);
1092     Time->Minute  = DecimalToBcd8 (Time->Minute);
1093     Time->Second  = DecimalToBcd8 (Time->Second);
1094   }
1095   //
1096   // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1097   //
1098   if (RegisterB.Bits.Mil == 0 && IsPM) {
1099     Time->Hour = (UINT8) (Time->Hour | 0x80);
1100   }
1101 }
1102 
1103 /**
1104   Compare the Hour, Minute and Second of the From time and the To time.
1105 
1106   Only compare H/M/S in EFI_TIME and ignore other fields here.
1107 
1108   @param From   the first time
1109   @param To     the second time
1110 
1111   @return  >0   The H/M/S of the From time is later than those of To time
1112   @return  ==0  The H/M/S of the From time is same as those of To time
1113   @return  <0   The H/M/S of the From time is earlier than those of To time
1114 **/
1115 INTN
CompareHMS(IN EFI_TIME * From,IN EFI_TIME * To)1116 CompareHMS (
1117   IN EFI_TIME   *From,
1118   IN EFI_TIME   *To
1119   )
1120 {
1121   if ((From->Hour > To->Hour) ||
1122      ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1123      ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {
1124     return 1;
1125   } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1126     return 0;
1127   } else {
1128     return -1;
1129   }
1130 }
1131 
1132 /**
1133   To check if second date is later than first date within 24 hours.
1134 
1135   @param  From   the first date
1136   @param  To     the second date
1137 
1138   @retval TRUE   From is previous to To within 24 hours.
1139   @retval FALSE  From is later, or it is previous to To more than 24 hours.
1140 **/
1141 BOOLEAN
IsWithinOneDay(IN EFI_TIME * From,IN EFI_TIME * To)1142 IsWithinOneDay (
1143   IN EFI_TIME  *From,
1144   IN EFI_TIME  *To
1145   )
1146 {
1147   UINT8   DayOfMonth[12];
1148   BOOLEAN Adjacent;
1149 
1150   DayOfMonth[0] = 31;
1151   DayOfMonth[1] = 29;
1152   DayOfMonth[2] = 31;
1153   DayOfMonth[3] = 30;
1154   DayOfMonth[4] = 31;
1155   DayOfMonth[5] = 30;
1156   DayOfMonth[6] = 31;
1157   DayOfMonth[7] = 31;
1158   DayOfMonth[8] = 30;
1159   DayOfMonth[9] = 31;
1160   DayOfMonth[10] = 30;
1161   DayOfMonth[11] = 31;
1162 
1163   Adjacent = FALSE;
1164 
1165   //
1166   // The validity of From->Month field should be checked before
1167   //
1168   ASSERT (From->Month >=1);
1169   ASSERT (From->Month <=12);
1170 
1171   if (From->Year == To->Year) {
1172     if (From->Month == To->Month) {
1173       if ((From->Day + 1) == To->Day) {
1174         if ((CompareHMS(From, To) >= 0)) {
1175           Adjacent = TRUE;
1176         }
1177       } else if (From->Day == To->Day) {
1178         if ((CompareHMS(From, To) <= 0)) {
1179           Adjacent = TRUE;
1180         }
1181       }
1182     } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1183       if ((From->Month == 2) && !IsLeapYear(From)) {
1184         if (From->Day == 28) {
1185           if ((CompareHMS(From, To) >= 0)) {
1186             Adjacent = TRUE;
1187           }
1188         }
1189       } else if (From->Day == DayOfMonth[From->Month - 1]) {
1190         if ((CompareHMS(From, To) >= 0)) {
1191            Adjacent = TRUE;
1192         }
1193       }
1194     }
1195   } else if (((From->Year + 1) == To->Year) &&
1196              (From->Month == 12) &&
1197              (From->Day   == 31) &&
1198              (To->Month   == 1)  &&
1199              (To->Day     == 1)) {
1200     if ((CompareHMS(From, To) >= 0)) {
1201       Adjacent = TRUE;
1202     }
1203   }
1204 
1205   return Adjacent;
1206 }
1207 
1208 /**
1209   This function find ACPI table with the specified signature in RSDT or XSDT.
1210 
1211   @param Sdt              ACPI RSDT or XSDT.
1212   @param Signature        ACPI table signature.
1213   @param TablePointerSize Size of table pointer: 4 or 8.
1214 
1215   @return ACPI table or NULL if not found.
1216 **/
1217 VOID *
ScanTableInSDT(IN EFI_ACPI_DESCRIPTION_HEADER * Sdt,IN UINT32 Signature,IN UINTN TablePointerSize)1218 ScanTableInSDT (
1219   IN EFI_ACPI_DESCRIPTION_HEADER    *Sdt,
1220   IN UINT32                         Signature,
1221   IN UINTN                          TablePointerSize
1222   )
1223 {
1224   UINTN                          Index;
1225   UINTN                          EntryCount;
1226   UINTN                          EntryBase;
1227   EFI_ACPI_DESCRIPTION_HEADER    *Table;
1228 
1229   EntryCount = (Sdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / TablePointerSize;
1230 
1231   EntryBase = (UINTN) (Sdt + 1);
1232   for (Index = 0; Index < EntryCount; Index++) {
1233     //
1234     // When TablePointerSize is 4 while sizeof (VOID *) is 8, make sure the upper 4 bytes are zero.
1235     //
1236     Table = 0;
1237     CopyMem (&Table, (VOID *) (EntryBase + Index * TablePointerSize), TablePointerSize);
1238     if (Table->Signature == Signature) {
1239       return Table;
1240     }
1241   }
1242 
1243   return NULL;
1244 }
1245 
1246 /**
1247   Notification function of ACPI Table change.
1248 
1249   This is a notification function registered on ACPI Table change event.
1250   It saves the Century address stored in ACPI FADT table.
1251 
1252   @param  Event        Event whose notification function is being invoked.
1253   @param  Context      Pointer to the notification function's context.
1254 
1255 **/
1256 VOID
1257 EFIAPI
PcRtcAcpiTableChangeCallback(IN EFI_EVENT Event,IN VOID * Context)1258 PcRtcAcpiTableChangeCallback (
1259   IN EFI_EVENT        Event,
1260   IN VOID             *Context
1261   )
1262 {
1263   EFI_STATUS                                    Status;
1264   EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp;
1265   EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt;
1266   EFI_ACPI_DESCRIPTION_HEADER                   *Xsdt;
1267   EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt;
1268   EFI_TIME                                      Time;
1269   UINT8                                         Century;
1270 
1271   Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **) &Rsdp);
1272   if (EFI_ERROR (Status)) {
1273     Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **) &Rsdp);
1274   }
1275 
1276   if (EFI_ERROR (Status)) {
1277     return;
1278   }
1279 
1280   ASSERT (Rsdp != NULL);
1281 
1282   //
1283   // Find FADT in XSDT
1284   //
1285   Fadt = NULL;
1286   if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
1287     Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->XsdtAddress;
1288     Fadt = ScanTableInSDT (Xsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, sizeof (UINT64));
1289   }
1290 
1291   if (Fadt == NULL) {
1292     //
1293     // Find FADT in RSDT
1294     //
1295     Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress;
1296     Fadt = ScanTableInSDT (Rsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, sizeof (UINT32));
1297   }
1298 
1299   if ((Fadt != NULL) &&
1300       (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80) &&
1301       (mModuleGlobal.CenturyRtcAddress != Fadt->Century)
1302       ) {
1303     mModuleGlobal.CenturyRtcAddress = Fadt->Century;
1304     Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);
1305     if (!EFI_ERROR (Status)) {
1306       Century = (UINT8) (Time.Year / 100);
1307       Century = DecimalToBcd8 (Century);
1308       DEBUG ((EFI_D_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));
1309       RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);
1310     }
1311   }
1312 }
1313