Home | History | Annotate | Download | only in PcatRealTimeClockRuntimeDxe
      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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 *
   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
   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