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