]> git.proxmox.com Git - mirror_edk2.git/blobdiff - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / PcAtChipsetPkg / PcatRealTimeClockRuntimeDxe / PcRtc.c
index 6c7e0ca6d77517f33efaa4d9f233ae99766fa7c4..9242a2e82600122620dffceb546bdb5e536a9368 100644 (file)
@@ -1,22 +1,32 @@
 /** @file\r
   RTC Architectural Protocol GUID as defined in DxeCis 0.96.\r
 \r
-Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
+Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.<BR>\r
 \r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include "PcRtc.h"\r
 \r
+extern UINTN  mRtcIndexRegister;\r
+extern UINTN  mRtcTargetRegister;\r
+\r
+//\r
+// Days of month.\r
+//\r
+UINTN  mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
+\r
+//\r
+// The name of NV variable to store the timezone and daylight saving information.\r
+//\r
+CHAR16  mTimeZoneVariableName[] = L"RTC";\r
+\r
 /**\r
   Compare the Hour, Minute and Second of the From time and the To time.\r
-  \r
+\r
   Only compare H/M/S in EFI_TIME and ignore other fields here.\r
 \r
   @param From   the first time\r
@@ -28,8 +38,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 **/\r
 INTN\r
 CompareHMS (\r
-  IN EFI_TIME   *From,\r
-  IN EFI_TIME   *To\r
+  IN EFI_TIME  *From,\r
+  IN EFI_TIME  *To\r
   );\r
 \r
 /**\r
@@ -43,43 +53,137 @@ CompareHMS (
 **/\r
 BOOLEAN\r
 IsWithinOneDay (\r
-  IN EFI_TIME   *From,\r
-  IN EFI_TIME   *To\r
+  IN EFI_TIME  *From,\r
+  IN EFI_TIME  *To\r
   );\r
 \r
+/**\r
+  Read RTC content through its registers using IO access.\r
+\r
+  @param  Address   Address offset of RTC. It is recommended to use\r
+                    macros such as RTC_ADDRESS_SECONDS.\r
+\r
+  @return The data of UINT8 type read from RTC.\r
+**/\r
+STATIC\r
+UINT8\r
+IoRtcRead (\r
+  IN  UINTN  Address\r
+  )\r
+{\r
+  IoWrite8 (\r
+    PcdGet8 (PcdRtcIndexRegister),\r
+    (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))\r
+    );\r
+  return IoRead8 (PcdGet8 (PcdRtcTargetRegister));\r
+}\r
+\r
+/**\r
+  Write RTC through its registers  using IO access.\r
+\r
+  @param  Address   Address offset of RTC. It is recommended to use\r
+                    macros such as RTC_ADDRESS_SECONDS.\r
+  @param  Data      The content you want to write into RTC.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+IoRtcWrite (\r
+  IN  UINTN  Address,\r
+  IN  UINT8  Data\r
+  )\r
+{\r
+  IoWrite8 (\r
+    PcdGet8 (PcdRtcIndexRegister),\r
+    (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))\r
+    );\r
+  IoWrite8 (PcdGet8 (PcdRtcTargetRegister), Data);\r
+}\r
+\r
+/**\r
+  Read RTC content through its registers using MMIO access.\r
+\r
+  @param  Address   Address offset of RTC. It is recommended to use\r
+                    macros such as RTC_ADDRESS_SECONDS.\r
+\r
+  @return The data of UINT8 type read from RTC.\r
+**/\r
+STATIC\r
+UINT8\r
+MmioRtcRead (\r
+  IN  UINTN  Address\r
+  )\r
+{\r
+  MmioWrite8 (\r
+    mRtcIndexRegister,\r
+    (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))\r
+    );\r
+  return MmioRead8 (mRtcTargetRegister);\r
+}\r
+\r
+/**\r
+  Write RTC through its registers using MMIO access.\r
+\r
+  @param  Address   Address offset of RTC. It is recommended to use\r
+                    macros such as RTC_ADDRESS_SECONDS.\r
+  @param  Data      The content you want to write into RTC.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+MmioRtcWrite (\r
+  IN  UINTN  Address,\r
+  IN  UINT8  Data\r
+  )\r
+{\r
+  MmioWrite8 (\r
+    mRtcIndexRegister,\r
+    (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))\r
+    );\r
+  MmioWrite8 (mRtcTargetRegister, Data);\r
+}\r
+\r
 /**\r
   Read RTC content through its registers.\r
 \r
-  @param  Address  Address offset of RTC. It is recommended to use macros such as\r
-                   RTC_ADDRESS_SECONDS.\r
+  @param  Address   Address offset of RTC. It is recommended to use\r
+                    macros such as RTC_ADDRESS_SECONDS.\r
 \r
   @return The data of UINT8 type read from RTC.\r
 **/\r
+STATIC\r
 UINT8\r
 RtcRead (\r
-  IN  UINT8 Address\r
+  IN  UINT Address\r
   )\r
 {\r
-  IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
-  return IoRead8 (PCAT_RTC_DATA_REGISTER);\r
+  if (FeaturePcdGet (PcdRtcUseMmio)) {\r
+    return MmioRtcRead (Address);\r
+  }\r
+\r
+  return IoRtcRead (Address);\r
 }\r
 \r
 /**\r
   Write RTC through its registers.\r
 \r
-  @param  Address  Address offset of RTC. It is recommended to use macros such as\r
-                   RTC_ADDRESS_SECONDS.\r
-  @param  Data     The content you want to write into RTC.\r
+  @param  Address   Address offset of RTC. It is recommended to use\r
+                    macros such as RTC_ADDRESS_SECONDS.\r
+  @param  Data      The content you want to write into RTC.\r
 \r
 **/\r
+STATIC\r
 VOID\r
 RtcWrite (\r
-  IN  UINT  Address,\r
-  IN  UINT8   Data\r
+  IN  UINTN  Address,\r
+  IN  UINT8  Data\r
   )\r
 {\r
-  IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
-  IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);\r
+  if (FeaturePcdGet (PcdRtcUseMmio)) {\r
+    MmioRtcWrite (Address, Data);\r
+  } else {\r
+    IoRtcWrite (Address, Data);\r
+  }\r
 }\r
 \r
 /**\r
@@ -100,10 +204,11 @@ PcRtcInit (
   RTC_REGISTER_A  RegisterA;\r
   RTC_REGISTER_B  RegisterB;\r
   RTC_REGISTER_D  RegisterD;\r
-  UINT8           Century;\r
   EFI_TIME        Time;\r
   UINTN           DataSize;\r
   UINT32          TimerVar;\r
+  BOOLEAN         Enabled;\r
+  BOOLEAN         Pending;\r
 \r
   //\r
   // Acquire RTC Lock to make access to RTC atomic\r
@@ -111,13 +216,14 @@ PcRtcInit (
   if (!EfiAtRuntime ()) {\r
     EfiAcquireLock (&Global->RtcLock);\r
   }\r
+\r
   //\r
   // Initialize RTC Register\r
   //\r
   // Make sure Division Chain is properly configured,\r
   // or RTC clock won't "tick" -- time won't increment\r
   //\r
-  RegisterA.Data = RTC_INIT_REGISTER_A;\r
+  RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);\r
   RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);\r
 \r
   //\r
@@ -133,7 +239,7 @@ PcRtcInit (
   //\r
   // Clear RTC register D\r
   //\r
-  RegisterD.Data = RTC_INIT_REGISTER_D;\r
+  RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);\r
   RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);\r
 \r
   //\r
@@ -141,11 +247,18 @@ PcRtcInit (
   //\r
   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
   if (EFI_ERROR (Status)) {\r
+    //\r
+    // Set the variable with default value if the RTC is functioning incorrectly.\r
+    //\r
+    Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
+    Global->Daylight      = 0;\r
     if (!EfiAtRuntime ()) {\r
       EfiReleaseLock (&Global->RtcLock);\r
     }\r
+\r
     return EFI_DEVICE_ERROR;\r
   }\r
+\r
   //\r
   // Get the Time/Date/Daylight Savings values.\r
   //\r
@@ -156,67 +269,165 @@ PcRtcInit (
   Time.Month  = RtcRead (RTC_ADDRESS_MONTH);\r
   Time.Year   = RtcRead (RTC_ADDRESS_YEAR);\r
 \r
-  Century = RtcRead (RTC_ADDRESS_CENTURY);\r
-  \r
-  //\r
-  // Set RTC configuration after get original time\r
-  // The value of bit AIE should be reserved.\r
-  //\r
-  RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));\r
-\r
   //\r
   // Release RTC Lock.\r
   //\r
   if (!EfiAtRuntime ()) {\r
     EfiReleaseLock (&Global->RtcLock);\r
   }\r
\r
+\r
   //\r
   // Get the data of Daylight saving and time zone, if they have been\r
   // stored in NV variable during previous boot.\r
   //\r
   DataSize = sizeof (UINT32);\r
-  Status = EfiGetVariable (\r
-             L"RTC",\r
-             &gEfiCallerIdGuid,\r
-             NULL,\r
-             &DataSize,\r
-             (VOID *) &TimerVar\r
-             );\r
+  Status   = EfiGetVariable (\r
+               mTimeZoneVariableName,\r
+               &gEfiCallerIdGuid,\r
+               NULL,\r
+               &DataSize,\r
+               &TimerVar\r
+               );\r
   if (!EFI_ERROR (Status)) {\r
-    Time.TimeZone = (INT16) TimerVar;\r
-    Time.Daylight = (UINT8) (TimerVar >> 16);\r
+    Time.TimeZone = (INT16)TimerVar;\r
+    Time.Daylight = (UINT8)(TimerVar >> 16);\r
   } else {\r
     Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
-    Time.Daylight = 0;  \r
+    Time.Daylight = 0;\r
   }\r
 \r
   //\r
   // Validate time fields\r
   //\r
-  Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB);\r
+  Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);\r
   if (!EFI_ERROR (Status)) {\r
     Status = RtcTimeFieldsValid (&Time);\r
   }\r
+\r
   if (EFI_ERROR (Status)) {\r
-    Time.Second = RTC_INIT_SECOND;\r
-    Time.Minute = RTC_INIT_MINUTE;\r
-    Time.Hour   = RTC_INIT_HOUR;\r
-    Time.Day    = RTC_INIT_DAY;\r
-    Time.Month  = RTC_INIT_MONTH;\r
-    Time.Year   = RTC_INIT_YEAR;\r
-    Time.Nanosecond  = 0;\r
+    //\r
+    // Report Status Code to indicate that the RTC has bad date and time\r
+    //\r
+    REPORT_STATUS_CODE (\r
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+      (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)\r
+      );\r
+    Time.Second     = RTC_INIT_SECOND;\r
+    Time.Minute     = RTC_INIT_MINUTE;\r
+    Time.Hour       = RTC_INIT_HOUR;\r
+    Time.Day        = RTC_INIT_DAY;\r
+    Time.Month      = RTC_INIT_MONTH;\r
+    Time.Year       = PcdGet16 (PcdMinimalValidYear);\r
+    Time.Nanosecond = 0;\r
+    Time.TimeZone   = EFI_UNSPECIFIED_TIMEZONE;\r
+    Time.Daylight   = 0;\r
   }\r
 \r
+  //\r
+  // Set RTC configuration after get original time\r
+  // The value of bit AIE should be reserved.\r
+  //\r
+  RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);\r
+  RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
+\r
   //\r
   // Reset time value according to new RTC configuration\r
   //\r
   Status = PcRtcSetTime (&Time, Global);\r
-  if(!EFI_ERROR (Status)) {\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.\r
+  // Global variable has already had valid SavedTimeZone and Daylight,\r
+  // so we can use them to get and set wakeup time.\r
+  //\r
+  Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);\r
+  if ((Enabled) || (!EFI_ERROR (Status))) {\r
     return EFI_SUCCESS;\r
-  } else {\r
+  }\r
+\r
+  //\r
+  // When wakeup time is disabled and invalid, reset wakeup time register to valid state\r
+  // but keep wakeup alarm disabled.\r
+  //\r
+  Time.Second     = RTC_INIT_SECOND;\r
+  Time.Minute     = RTC_INIT_MINUTE;\r
+  Time.Hour       = RTC_INIT_HOUR;\r
+  Time.Day        = RTC_INIT_DAY;\r
+  Time.Month      = RTC_INIT_MONTH;\r
+  Time.Year       = PcdGet16 (PcdMinimalValidYear);\r
+  Time.Nanosecond = 0;\r
+  Time.TimeZone   = Global->SavedTimeZone;\r
+  Time.Daylight   = Global->Daylight;\r
+\r
+  //\r
+  // Acquire RTC Lock to make access to RTC atomic\r
+  //\r
+  if (!EfiAtRuntime ()) {\r
+    EfiAcquireLock (&Global->RtcLock);\r
+  }\r
+\r
+  //\r
+  // Wait for up to 0.1 seconds for the RTC to be updated\r
+  //\r
+  Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
+  if (EFI_ERROR (Status)) {\r
+    if (!EfiAtRuntime ()) {\r
+      EfiReleaseLock (&Global->RtcLock);\r
+    }\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  ConvertEfiTimeToRtcTime (&Time, RegisterB);\r
+\r
+  //\r
+  // Set the Y/M/D info to variable as it has no corresponding hw registers.\r
+  //\r
+  Status =  EfiSetVariable (\r
+              L"RTCALARM",\r
+              &gEfiCallerIdGuid,\r
+              EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+              sizeof (Time),\r
+              &Time\r
+              );\r
+  if (EFI_ERROR (Status)) {\r
+    if (!EfiAtRuntime ()) {\r
+      EfiReleaseLock (&Global->RtcLock);\r
+    }\r
+\r
     return EFI_DEVICE_ERROR;\r
   }\r
+\r
+  //\r
+  // Inhibit updates of the RTC\r
+  //\r
+  RegisterB.Bits.Set = 1;\r
+  RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
+\r
+  //\r
+  // Set RTC alarm time registers\r
+  //\r
+  RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);\r
+  RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);\r
+  RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);\r
+\r
+  //\r
+  // Allow updates of the RTC registers\r
+  //\r
+  RegisterB.Bits.Set = 0;\r
+  RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
+\r
+  //\r
+  // Release RTC Lock.\r
+  //\r
+  if (!EfiAtRuntime ()) {\r
+    EfiReleaseLock (&Global->RtcLock);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -236,37 +447,39 @@ PcRtcInit (
 EFI_STATUS\r
 PcRtcGetTime (\r
   OUT  EFI_TIME               *Time,\r
-  OUT  EFI_TIME_CAPABILITIES  *Capabilities,  OPTIONAL\r
+  OUT  EFI_TIME_CAPABILITIES  *Capabilities   OPTIONAL,\r
   IN   PC_RTC_MODULE_GLOBALS  *Global\r
   )\r
 {\r
   EFI_STATUS      Status;\r
   RTC_REGISTER_B  RegisterB;\r
-  UINT8           Century;\r
 \r
   //\r
   // Check parameters for null pointer\r
   //\r
   if (Time == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
-\r
   }\r
+\r
   //\r
   // Acquire RTC Lock to make access to RTC atomic\r
   //\r
   if (!EfiAtRuntime ()) {\r
     EfiAcquireLock (&Global->RtcLock);\r
   }\r
+\r
   //\r
   // Wait for up to 0.1 seconds for the RTC to be updated\r
   //\r
   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
   if (EFI_ERROR (Status)) {\r
-      if (!EfiAtRuntime ()) {\r
-        EfiReleaseLock (&Global->RtcLock);\r
-      }\r
+    if (!EfiAtRuntime ()) {\r
+      EfiReleaseLock (&Global->RtcLock);\r
+    }\r
+\r
     return Status;\r
   }\r
+\r
   //\r
   // Read Register B\r
   //\r
@@ -275,15 +488,13 @@ PcRtcGetTime (
   //\r
   // Get the Time/Date/Daylight Savings values.\r
   //\r
-  Time->Second  = RtcRead (RTC_ADDRESS_SECONDS);\r
-  Time->Minute  = RtcRead (RTC_ADDRESS_MINUTES);\r
-  Time->Hour    = RtcRead (RTC_ADDRESS_HOURS);\r
-  Time->Day     = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
-  Time->Month   = RtcRead (RTC_ADDRESS_MONTH);\r
-  Time->Year    = RtcRead (RTC_ADDRESS_YEAR);\r
+  Time->Second = RtcRead (RTC_ADDRESS_SECONDS);\r
+  Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
+  Time->Hour   = RtcRead (RTC_ADDRESS_HOURS);\r
+  Time->Day    = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
+  Time->Month  = RtcRead (RTC_ADDRESS_MONTH);\r
+  Time->Year   = RtcRead (RTC_ADDRESS_YEAR);\r
 \r
-  Century = RtcRead (RTC_ADDRESS_CENTURY);\r
-  \r
   //\r
   // Release RTC Lock.\r
   //\r
@@ -294,16 +505,17 @@ PcRtcGetTime (
   //\r
   // Get the variable that contains the TimeZone and Daylight fields\r
   //\r
-  Time->TimeZone  = Global->SavedTimeZone;\r
-  Time->Daylight  = Global->Daylight;\r
+  Time->TimeZone = Global->SavedTimeZone;\r
+  Time->Daylight = Global->Daylight;\r
 \r
   //\r
   // Make sure all field values are in correct range\r
   //\r
-  Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);\r
+  Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
   if (!EFI_ERROR (Status)) {\r
     Status = RtcTimeFieldsValid (Time);\r
   }\r
+\r
   if (EFI_ERROR (Status)) {\r
     return EFI_DEVICE_ERROR;\r
   }\r
@@ -339,19 +551,19 @@ PcRtcGetTime (
 **/\r
 EFI_STATUS\r
 PcRtcSetTime (\r
-  IN EFI_TIME                *Time,\r
-  IN PC_RTC_MODULE_GLOBALS   *Global\r
+  IN EFI_TIME               *Time,\r
+  IN PC_RTC_MODULE_GLOBALS  *Global\r
   )\r
 {\r
   EFI_STATUS      Status;\r
   EFI_TIME        RtcTime;\r
   RTC_REGISTER_B  RegisterB;\r
-  UINT8           Century;\r
   UINT32          TimerVar;\r
 \r
   if (Time == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
+\r
   //\r
   // Make sure that the time fields are valid\r
   //\r
@@ -368,32 +580,75 @@ PcRtcSetTime (
   if (!EfiAtRuntime ()) {\r
     EfiAcquireLock (&Global->RtcLock);\r
   }\r
+\r
   //\r
   // Wait for up to 0.1 seconds for the RTC to be updated\r
   //\r
   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
   if (EFI_ERROR (Status)) {\r
-     if (!EfiAtRuntime ()) {\r
-       EfiReleaseLock (&Global->RtcLock);\r
-     }\r
+    if (!EfiAtRuntime ()) {\r
+      EfiReleaseLock (&Global->RtcLock);\r
+    }\r
+\r
     return Status;\r
   }\r
+\r
+  //\r
+  // Write timezone and daylight to RTC variable\r
+  //\r
+  if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {\r
+    Status = EfiSetVariable (\r
+               mTimeZoneVariableName,\r
+               &gEfiCallerIdGuid,\r
+               0,\r
+               0,\r
+               NULL\r
+               );\r
+    if (Status == EFI_NOT_FOUND) {\r
+      Status = EFI_SUCCESS;\r
+    }\r
+  } else {\r
+    TimerVar = Time->Daylight;\r
+    TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(Time->TimeZone));\r
+    Status   = EfiSetVariable (\r
+                 mTimeZoneVariableName,\r
+                 &gEfiCallerIdGuid,\r
+                 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                 sizeof (TimerVar),\r
+                 &TimerVar\r
+                 );\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    if (!EfiAtRuntime ()) {\r
+      EfiReleaseLock (&Global->RtcLock);\r
+    }\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
   //\r
   // Read Register B, and inhibit updates of the RTC\r
   //\r
-  RegisterB.Data      = RtcRead (RTC_ADDRESS_REGISTER_B);\r
-  RegisterB.Bits.Set  = 1;\r
+  RegisterB.Data     = RtcRead (RTC_ADDRESS_REGISTER_B);\r
+  RegisterB.Bits.Set = 1;\r
   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
 \r
-  ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
+  //\r
+  // Store the century value to RTC before converting to BCD format.\r
+  //\r
+  if (Global->CenturyRtcAddress != 0) {\r
+    RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));\r
+  }\r
+\r
+  ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
 \r
   RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r
   RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);\r
   RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);\r
   RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);\r
   RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);\r
-  RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);\r
-  RtcWrite (RTC_ADDRESS_CENTURY, Century);\r
+  RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year);\r
 \r
   //\r
   // Allow updates of the RTC registers\r
@@ -407,23 +662,13 @@ PcRtcSetTime (
   if (!EfiAtRuntime ()) {\r
     EfiReleaseLock (&Global->RtcLock);\r
   }\r
+\r
   //\r
   // Set the variable that contains the TimeZone and Daylight fields\r
   //\r
   Global->SavedTimeZone = Time->TimeZone;\r
   Global->Daylight      = Time->Daylight;\r
 \r
-  TimerVar = Time->Daylight;\r
-  TimerVar = (UINT32) ((TimerVar << 16) | Time->TimeZone);\r
-  Status =  EfiSetVariable (\r
-              L"RTC",\r
-              &gEfiCallerIdGuid,\r
-              EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
-              sizeof (TimerVar),\r
-              &TimerVar\r
-              );\r
-  ASSERT_EFI_ERROR (Status);\r
-\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -454,59 +699,76 @@ PcRtcGetWakeupTime (
   EFI_STATUS      Status;\r
   RTC_REGISTER_B  RegisterB;\r
   RTC_REGISTER_C  RegisterC;\r
-  UINT8           Century;\r
+  EFI_TIME        RtcTime;\r
+  UINTN           DataSize;\r
 \r
   //\r
   // Check parameters for null pointers\r
   //\r
   if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
-\r
   }\r
+\r
   //\r
   // Acquire RTC Lock to make access to RTC atomic\r
   //\r
   if (!EfiAtRuntime ()) {\r
     EfiAcquireLock (&Global->RtcLock);\r
   }\r
+\r
   //\r
   // Wait for up to 0.1 seconds for the RTC to be updated\r
   //\r
   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
   if (EFI_ERROR (Status)) {\r
     if (!EfiAtRuntime ()) {\r
-    EfiReleaseLock (&Global->RtcLock);\r
+      EfiReleaseLock (&Global->RtcLock);\r
     }\r
+\r
     return EFI_DEVICE_ERROR;\r
   }\r
+\r
   //\r
   // Read Register B and Register C\r
   //\r
-  RegisterB.Data  = RtcRead (RTC_ADDRESS_REGISTER_B);\r
-  RegisterC.Data  = RtcRead (RTC_ADDRESS_REGISTER_C);\r
+  RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
+  RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r
 \r
   //\r
   // Get the Time/Date/Daylight Savings values.\r
   //\r
   *Enabled = RegisterB.Bits.Aie;\r
-  if (*Enabled) {\r
-    Time->Second  = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
-    Time->Minute  = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
-    Time->Hour    = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
-    Time->Day     = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
-    Time->Month   = RtcRead (RTC_ADDRESS_MONTH);\r
-    Time->Year    = RtcRead (RTC_ADDRESS_YEAR);\r
-  } else {\r
-    Time->Second  = 0;\r
-    Time->Minute  = 0;\r
-    Time->Hour    = 0;\r
-    Time->Day     = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
-    Time->Month   = RtcRead (RTC_ADDRESS_MONTH);\r
-    Time->Year    = RtcRead (RTC_ADDRESS_YEAR);\r
+  *Pending = RegisterC.Bits.Af;\r
+\r
+  Time->Second   = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
+  Time->Minute   = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
+  Time->Hour     = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
+  Time->Day      = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
+  Time->Month    = RtcRead (RTC_ADDRESS_MONTH);\r
+  Time->Year     = RtcRead (RTC_ADDRESS_YEAR);\r
+  Time->TimeZone = Global->SavedTimeZone;\r
+  Time->Daylight = Global->Daylight;\r
+\r
+  //\r
+  // Get the alarm info from variable\r
+  //\r
+  DataSize = sizeof (EFI_TIME);\r
+  Status   = EfiGetVariable (\r
+               L"RTCALARM",\r
+               &gEfiCallerIdGuid,\r
+               NULL,\r
+               &DataSize,\r
+               &RtcTime\r
+               );\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // The alarm variable exists. In this case, we read variable to get info.\r
+    //\r
+    Time->Day   = RtcTime.Day;\r
+    Time->Month = RtcTime.Month;\r
+    Time->Year  = RtcTime.Year;\r
   }\r
 \r
-  Century = RtcRead (RTC_ADDRESS_CENTURY);\r
-  \r
   //\r
   // Release RTC Lock.\r
   //\r
@@ -514,25 +776,18 @@ PcRtcGetWakeupTime (
     EfiReleaseLock (&Global->RtcLock);\r
   }\r
 \r
-  //\r
-  // Get the variable that contains the TimeZone and Daylight fields\r
-  //\r
-  Time->TimeZone  = Global->SavedTimeZone;\r
-  Time->Daylight  = Global->Daylight;\r
-\r
   //\r
   // Make sure all field values are in correct range\r
   //\r
-  Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);\r
+  Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
   if (!EFI_ERROR (Status)) {\r
     Status = RtcTimeFieldsValid (Time);\r
   }\r
+\r
   if (EFI_ERROR (Status)) {\r
     return EFI_DEVICE_ERROR;\r
   }\r
 \r
-  *Pending = RegisterC.Bits.Af;\r
-\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -554,21 +809,22 @@ PcRtcGetWakeupTime (
 EFI_STATUS\r
 PcRtcSetWakeupTime (\r
   IN BOOLEAN                Enable,\r
-  IN EFI_TIME               *Time,   OPTIONAL\r
+  IN EFI_TIME               *Time    OPTIONAL,\r
   IN PC_RTC_MODULE_GLOBALS  *Global\r
   )\r
 {\r
-  EFI_STATUS            Status;\r
-  EFI_TIME              RtcTime;\r
-  RTC_REGISTER_B        RegisterB;\r
-  UINT8                 Century;\r
-  EFI_TIME_CAPABILITIES Capabilities;\r
+  EFI_STATUS             Status;\r
+  EFI_TIME               RtcTime;\r
+  RTC_REGISTER_B         RegisterB;\r
+  EFI_TIME_CAPABILITIES  Capabilities;\r
 \r
-  if (Enable) {\r
+  ZeroMem (&RtcTime, sizeof (RtcTime));\r
 \r
+  if (Enable) {\r
     if (Time == NULL) {\r
       return EFI_INVALID_PARAMETER;\r
     }\r
+\r
     //\r
     // Make sure that the time fields are valid\r
     //\r
@@ -576,6 +832,7 @@ PcRtcSetWakeupTime (
     if (EFI_ERROR (Status)) {\r
       return EFI_INVALID_PARAMETER;\r
     }\r
+\r
     //\r
     // Just support set alarm time within 24 hours\r
     //\r
@@ -584,42 +841,82 @@ PcRtcSetWakeupTime (
     if (EFI_ERROR (Status)) {\r
       return EFI_DEVICE_ERROR;\r
     }\r
+\r
     if (!IsWithinOneDay (&RtcTime, Time)) {\r
       return EFI_UNSUPPORTED;\r
     }\r
+\r
     //\r
     // Make a local copy of the time and date\r
     //\r
     CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
-\r
   }\r
+\r
   //\r
   // Acquire RTC Lock to make access to RTC atomic\r
   //\r
   if (!EfiAtRuntime ()) {\r
     EfiAcquireLock (&Global->RtcLock);\r
   }\r
+\r
   //\r
   // Wait for up to 0.1 seconds for the RTC to be updated\r
   //\r
   Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
   if (EFI_ERROR (Status)) {\r
     if (!EfiAtRuntime ()) {\r
-    EfiReleaseLock (&Global->RtcLock);\r
+      EfiReleaseLock (&Global->RtcLock);\r
     }\r
+\r
     return EFI_DEVICE_ERROR;\r
   }\r
+\r
   //\r
-  // Read Register B, and inhibit updates of the RTC\r
+  // Read Register B\r
   //\r
-  RegisterB.Data      = RtcRead (RTC_ADDRESS_REGISTER_B);\r
+  RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
 \r
-  RegisterB.Bits.Set  = 1;\r
+  if (Enable) {\r
+    ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
+  } else {\r
+    //\r
+    // if the alarm is disable, record the current setting.\r
+    //\r
+    RtcTime.Second   = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
+    RtcTime.Minute   = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
+    RtcTime.Hour     = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
+    RtcTime.Day      = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
+    RtcTime.Month    = RtcRead (RTC_ADDRESS_MONTH);\r
+    RtcTime.Year     = RtcRead (RTC_ADDRESS_YEAR);\r
+    RtcTime.TimeZone = Global->SavedTimeZone;\r
+    RtcTime.Daylight = Global->Daylight;\r
+  }\r
+\r
+  //\r
+  // Set the Y/M/D info to variable as it has no corresponding hw registers.\r
+  //\r
+  Status =  EfiSetVariable (\r
+              L"RTCALARM",\r
+              &gEfiCallerIdGuid,\r
+              EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+              sizeof (RtcTime),\r
+              &RtcTime\r
+              );\r
+  if (EFI_ERROR (Status)) {\r
+    if (!EfiAtRuntime ()) {\r
+      EfiReleaseLock (&Global->RtcLock);\r
+    }\r
+\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Inhibit updates of the RTC\r
+  //\r
+  RegisterB.Bits.Set = 1;\r
   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
 \r
   if (Enable) {\r
-    ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
-\r
     //\r
     // Set RTC alarm time\r
     //\r
@@ -628,10 +925,10 @@ PcRtcSetWakeupTime (
     RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);\r
 \r
     RegisterB.Bits.Aie = 1;\r
-\r
   } else {\r
     RegisterB.Bits.Aie = 0;\r
   }\r
+\r
   //\r
   // Allow updates of the RTC registers\r
   //\r
@@ -644,10 +941,10 @@ PcRtcSetWakeupTime (
   if (!EfiAtRuntime ()) {\r
     EfiReleaseLock (&Global->RtcLock);\r
   }\r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
-\r
 /**\r
   Checks an 8-bit BCD value, and converts to an 8-bit value if valid.\r
 \r
@@ -682,7 +979,6 @@ CheckAndConvertBcd8ToDecimal8 (
 \r
   @param   Time       On input, the time data read from RTC to convert\r
                       On output, the time converted to UEFI format\r
-  @param   Century    Value of century read from RTC.\r
   @param   RegisterB  Value of Register B of RTC, indicating data mode\r
                       and hour format.\r
 \r
@@ -693,52 +989,64 @@ CheckAndConvertBcd8ToDecimal8 (
 EFI_STATUS\r
 ConvertRtcTimeToEfiTime (\r
   IN OUT EFI_TIME        *Time,\r
-  IN     UINT8           Century,\r
   IN     RTC_REGISTER_B  RegisterB\r
   )\r
 {\r
-  BOOLEAN IsPM;\r
+  BOOLEAN  IsPM;\r
+  UINT8    Century;\r
 \r
-  if ((Time->Hour & 0x80) != 0) {\r
-    IsPM = TRUE;\r
-  } else {\r
-    IsPM = FALSE;\r
-  }\r
+  // IsPM only makes sense for 12-hour format.\r
+  if (RegisterB.Bits.Mil == 0) {\r
+    if ((Time->Hour & 0x80) != 0) {\r
+      IsPM = TRUE;\r
+    } else {\r
+      IsPM = FALSE;\r
+    }\r
 \r
-  Time->Hour = (UINT8) (Time->Hour & 0x7f);\r
+    Time->Hour = (UINT8)(Time->Hour & 0x7f);\r
+  }\r
 \r
   if (RegisterB.Bits.Dm == 0) {\r
-    Time->Year    = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);\r
-    Time->Month   = CheckAndConvertBcd8ToDecimal8 (Time->Month);\r
-    Time->Day     = CheckAndConvertBcd8ToDecimal8 (Time->Day);\r
-    Time->Hour    = CheckAndConvertBcd8ToDecimal8 (Time->Hour);\r
-    Time->Minute  = CheckAndConvertBcd8ToDecimal8 (Time->Minute);\r
-    Time->Second  = CheckAndConvertBcd8ToDecimal8 (Time->Second);\r
+    Time->Year   = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);\r
+    Time->Month  = CheckAndConvertBcd8ToDecimal8 (Time->Month);\r
+    Time->Day    = CheckAndConvertBcd8ToDecimal8 (Time->Day);\r
+    Time->Hour   = CheckAndConvertBcd8ToDecimal8 (Time->Hour);\r
+    Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);\r
+    Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);\r
   }\r
-  Century       = CheckAndConvertBcd8ToDecimal8 (Century);\r
 \r
-  if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||\r
-      Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff ||\r
-      Century == 0xff) {\r
+  if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||\r
+      (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))\r
+  {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Time->Year = (UINT16) (Century * 100 + Time->Year);\r
+  //\r
+  // For minimal/maximum year range [1970, 2069],\r
+  //   Century is 19 if RTC year >= 70,\r
+  //   Century is 20 otherwise.\r
+  //\r
+  Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100);\r
+  if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {\r
+    Century++;\r
+  }\r
+\r
+  Time->Year = (UINT16)(Century * 100 + Time->Year);\r
 \r
   //\r
   // If time is in 12 hour format, convert it to 24 hour format\r
   //\r
   if (RegisterB.Bits.Mil == 0) {\r
-    if (IsPM && Time->Hour < 12) {\r
-      Time->Hour = (UINT8) (Time->Hour + 12);\r
+    if (IsPM && (Time->Hour < 12)) {\r
+      Time->Hour = (UINT8)(Time->Hour + 12);\r
     }\r
 \r
-    if (!IsPM && Time->Hour == 12) {\r
+    if (!IsPM && (Time->Hour == 12)) {\r
       Time->Hour = 0;\r
     }\r
   }\r
 \r
-  Time->Nanosecond  = 0;\r
+  Time->Nanosecond = 0;\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -749,11 +1057,11 @@ ConvertRtcTimeToEfiTime (
   @param    Timeout  Tell how long it should take to wait.\r
 \r
   @retval   EFI_DEVICE_ERROR   RTC device error.\r
-  @retval   EFI_SUCCESS        RTC is updated and ready.  \r
+  @retval   EFI_SUCCESS        RTC is updated and ready.\r
 **/\r
 EFI_STATUS\r
 RtcWaitToUpdate (\r
-  UINTN Timeout\r
+  UINTN  Timeout\r
   )\r
 {\r
   RTC_REGISTER_A  RegisterA;\r
@@ -767,11 +1075,12 @@ RtcWaitToUpdate (
   if (RegisterD.Bits.Vrt == 0) {\r
     return EFI_DEVICE_ERROR;\r
   }\r
+\r
   //\r
   // Wait for up to 0.1 seconds for the RTC to be ready.\r
   //\r
-  Timeout         = (Timeout / 10) + 1;\r
-  RegisterA.Data  = RtcRead (RTC_ADDRESS_REGISTER_A);\r
+  Timeout        = (Timeout / 10) + 1;\r
+  RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
   while (RegisterA.Bits.Uip == 1 && Timeout > 0) {\r
     MicroSecondDelay (10);\r
     RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
@@ -779,7 +1088,7 @@ RtcWaitToUpdate (
   }\r
 \r
   RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
-  if (Timeout == 0 || RegisterD.Bits.Vrt == 0) {\r
+  if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {\r
     return EFI_DEVICE_ERROR;\r
   }\r
 \r
@@ -797,20 +1106,21 @@ RtcWaitToUpdate (
 **/\r
 EFI_STATUS\r
 RtcTimeFieldsValid (\r
-  IN EFI_TIME *Time\r
+  IN EFI_TIME  *Time\r
   )\r
 {\r
-  if (Time->Year < 1998 ||\r
-      Time->Year > 2099 ||\r
-      Time->Month < 1 ||\r
-      Time->Month > 12 ||\r
+  if ((Time->Year < PcdGet16 (PcdMinimalValidYear)) ||\r
+      (Time->Year > PcdGet16 (PcdMaximalValidYear)) ||\r
+      (Time->Month < 1) ||\r
+      (Time->Month > 12) ||\r
       (!DayValid (Time)) ||\r
-      Time->Hour > 23 ||\r
-      Time->Minute > 59 ||\r
-      Time->Second > 59 ||\r
-      Time->Nanosecond > 999999999 ||\r
-      (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||\r
-      ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {\r
+      (Time->Hour > 23) ||\r
+      (Time->Minute > 59) ||\r
+      (Time->Second > 59) ||\r
+      (Time->Nanosecond > 999999999) ||\r
+      (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||\r
+      ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))\r
+  {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -830,30 +1140,16 @@ DayValid (
   IN  EFI_TIME  *Time\r
   )\r
 {\r
-  INTN  DayOfMonth[12];\r
-\r
-  DayOfMonth[0] = 31;\r
-  DayOfMonth[1] = 29;\r
-  DayOfMonth[2] = 31;\r
-  DayOfMonth[3] = 30;\r
-  DayOfMonth[4] = 31;\r
-  DayOfMonth[5] = 30;\r
-  DayOfMonth[6] = 31;\r
-  DayOfMonth[7] = 31;\r
-  DayOfMonth[8] = 30;\r
-  DayOfMonth[9] = 31;\r
-  DayOfMonth[10] = 30;\r
-  DayOfMonth[11] = 31;\r
-\r
   //\r
   // The validity of Time->Month field should be checked before\r
   //\r
-  ASSERT (Time->Month >=1);\r
-  ASSERT (Time->Month <=12);\r
-  if (Time->Day < 1 ||\r
-      Time->Day > DayOfMonth[Time->Month - 1] ||\r
-      (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))\r
-      ) {\r
+  ASSERT (Time->Month >= 1);\r
+  ASSERT (Time->Month <= 12);\r
+  if ((Time->Day < 1) ||\r
+      (Time->Day > mDayOfMonth[Time->Month - 1]) ||\r
+      ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))\r
+      )\r
+  {\r
     return FALSE;\r
   }\r
 \r
@@ -870,7 +1166,7 @@ DayValid (
 **/\r
 BOOLEAN\r
 IsLeapYear (\r
-  IN EFI_TIME   *Time\r
+  IN EFI_TIME  *Time\r
   )\r
 {\r
   if (Time->Year % 4 == 0) {\r
@@ -889,26 +1185,23 @@ IsLeapYear (
 }\r
 \r
 /**\r
-  Converts time from EFI_TIME format defined by UEFI spec to RTC's.\r
+  Converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
 \r
-  This function converts time from EFI_TIME format defined by UEFI spec to RTC's.\r
+  This function converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
   If data mode of RTC is BCD, then converts EFI_TIME to it.\r
   If RTC is in 12-hour format, then converts EFI_TIME to it.\r
 \r
   @param   Time       On input, the time data read from UEFI to convert\r
                       On output, the time converted to RTC format\r
   @param   RegisterB  Value of Register B of RTC, indicating data mode\r
-  @param   Century    It is set according to EFI_TIME Time.\r
-\r
 **/\r
 VOID\r
 ConvertEfiTimeToRtcTime (\r
   IN OUT EFI_TIME        *Time,\r
-  IN     RTC_REGISTER_B  RegisterB,\r
-     OUT UINT8           *Century\r
+  IN     RTC_REGISTER_B  RegisterB\r
   )\r
 {\r
-  BOOLEAN IsPM;\r
+  BOOLEAN  IsPM;\r
 \r
   IsPM = TRUE;\r
   //\r
@@ -920,37 +1213,37 @@ ConvertEfiTimeToRtcTime (
     }\r
 \r
     if (Time->Hour >= 13) {\r
-      Time->Hour = (UINT8) (Time->Hour - 12);\r
+      Time->Hour = (UINT8)(Time->Hour - 12);\r
     } else if (Time->Hour == 0) {\r
       Time->Hour = 12;\r
     }\r
   }\r
+\r
   //\r
-  // Set the Time/Date/Daylight Savings values.\r
+  // Set the Time/Date values.\r
   //\r
-  *Century    = DecimalToBcd8 ((UINT8) (Time->Year / 100));\r
-\r
-  Time->Year  = (UINT16) (Time->Year % 100);\r
+  Time->Year = (UINT16)(Time->Year % 100);\r
 \r
   if (RegisterB.Bits.Dm == 0) {\r
-    Time->Year    = DecimalToBcd8 ((UINT8) Time->Year);\r
-    Time->Month   = DecimalToBcd8 (Time->Month);\r
-    Time->Day     = DecimalToBcd8 (Time->Day);\r
-    Time->Hour    = DecimalToBcd8 (Time->Hour);\r
-    Time->Minute  = DecimalToBcd8 (Time->Minute);\r
-    Time->Second  = DecimalToBcd8 (Time->Second);\r
+    Time->Year   = DecimalToBcd8 ((UINT8)Time->Year);\r
+    Time->Month  = DecimalToBcd8 (Time->Month);\r
+    Time->Day    = DecimalToBcd8 (Time->Day);\r
+    Time->Hour   = DecimalToBcd8 (Time->Hour);\r
+    Time->Minute = DecimalToBcd8 (Time->Minute);\r
+    Time->Second = DecimalToBcd8 (Time->Second);\r
   }\r
+\r
   //\r
   // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r
   //\r
-  if (RegisterB.Bits.Mil == 0 && IsPM) {\r
-    Time->Hour = (UINT8) (Time->Hour | 0x80);\r
+  if ((RegisterB.Bits.Mil == 0) && IsPM) {\r
+    Time->Hour = (UINT8)(Time->Hour | 0x80);\r
   }\r
 }\r
 \r
 /**\r
   Compare the Hour, Minute and Second of the From time and the To time.\r
-  \r
+\r
   Only compare H/M/S in EFI_TIME and ignore other fields here.\r
 \r
   @param From   the first time\r
@@ -962,13 +1255,14 @@ ConvertEfiTimeToRtcTime (
 **/\r
 INTN\r
 CompareHMS (\r
-  IN EFI_TIME   *From,\r
-  IN EFI_TIME   *To\r
+  IN EFI_TIME  *From,\r
+  IN EFI_TIME  *To\r
   )\r
 {\r
   if ((From->Hour > To->Hour) ||\r
-     ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||\r
-     ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {\r
+      ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||\r
+      ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))\r
+  {\r
     return 1;\r
   } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {\r
     return 0;\r
@@ -992,51 +1286,37 @@ IsWithinOneDay (
   IN EFI_TIME  *To\r
   )\r
 {\r
-  UINT8   DayOfMonth[12];\r
-  BOOLEAN Adjacent;\r
-\r
-  DayOfMonth[0] = 31;\r
-  DayOfMonth[1] = 29;\r
-  DayOfMonth[2] = 31;\r
-  DayOfMonth[3] = 30;\r
-  DayOfMonth[4] = 31;\r
-  DayOfMonth[5] = 30;\r
-  DayOfMonth[6] = 31;\r
-  DayOfMonth[7] = 31;\r
-  DayOfMonth[8] = 30;\r
-  DayOfMonth[9] = 31;\r
-  DayOfMonth[10] = 30;\r
-  DayOfMonth[11] = 31;\r
+  BOOLEAN  Adjacent;\r
 \r
   Adjacent = FALSE;\r
 \r
   //\r
   // The validity of From->Month field should be checked before\r
   //\r
-  ASSERT (From->Month >=1);\r
-  ASSERT (From->Month <=12);\r
-  \r
+  ASSERT (From->Month >= 1);\r
+  ASSERT (From->Month <= 12);\r
+\r
   if (From->Year == To->Year) {\r
     if (From->Month == To->Month) {\r
       if ((From->Day + 1) == To->Day) {\r
-        if ((CompareHMS(From, To) >= 0)) {\r
+        if ((CompareHMS (From, To) >= 0)) {\r
           Adjacent = TRUE;\r
         }\r
       } else if (From->Day == To->Day) {\r
-        if ((CompareHMS(From, To) <= 0)) {\r
+        if ((CompareHMS (From, To) <= 0)) {\r
           Adjacent = TRUE;\r
         }\r
       }\r
     } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {\r
-      if ((From->Month == 2) && !IsLeapYear(From)) {\r
+      if ((From->Month == 2) && !IsLeapYear (From)) {\r
         if (From->Day == 28) {\r
-          if ((CompareHMS(From, To) >= 0)) {\r
+          if ((CompareHMS (From, To) >= 0)) {\r
             Adjacent = TRUE;\r
           }\r
         }\r
-      } else if (From->Day == DayOfMonth[From->Month - 1]) {\r
-        if ((CompareHMS(From, To) >= 0)) {\r
-           Adjacent = TRUE;\r
+      } else if (From->Day == mDayOfMonth[From->Month - 1]) {\r
+        if ((CompareHMS (From, To) >= 0)) {\r
+          Adjacent = TRUE;\r
         }\r
       }\r
     }\r
@@ -1044,8 +1324,9 @@ IsWithinOneDay (
              (From->Month == 12) &&\r
              (From->Day   == 31) &&\r
              (To->Month   == 1)  &&\r
-             (To->Day     == 1)) {\r
-    if ((CompareHMS(From, To) >= 0)) {\r
+             (To->Day     == 1))\r
+  {\r
+    if ((CompareHMS (From, To) >= 0)) {\r
       Adjacent = TRUE;\r
     }\r
   }\r
@@ -1053,3 +1334,63 @@ IsWithinOneDay (
   return Adjacent;\r
 }\r
 \r
+/**\r
+  Get the century RTC address from the ACPI FADT table.\r
+\r
+  @return  The century RTC address or 0 if not found.\r
+**/\r
+UINT8\r
+GetCenturyRtcAddress (\r
+  VOID\r
+  )\r
+{\r
+  EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE  *Fadt;\r
+\r
+  Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (\r
+                                                        EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE\r
+                                                        );\r
+\r
+  if ((Fadt != NULL) &&\r
+      (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)\r
+      )\r
+  {\r
+    return Fadt->Century;\r
+  } else {\r
+    return 0;\r
+  }\r
+}\r
+\r
+/**\r
+  Notification function of ACPI Table change.\r
+\r
+  This is a notification function registered on ACPI Table change event.\r
+  It saves the Century address stored in ACPI FADT table.\r
+\r
+  @param  Event        Event whose notification function is being invoked.\r
+  @param  Context      Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PcRtcAcpiTableChangeCallback (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_TIME    Time;\r
+  UINT8       CenturyRtcAddress;\r
+  UINT8       Century;\r
+\r
+  CenturyRtcAddress = GetCenturyRtcAddress ();\r
+  if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {\r
+    mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;\r
+    Status                          = PcRtcGetTime (&Time, NULL, &mModuleGlobal);\r
+    if (!EFI_ERROR (Status)) {\r
+      Century = (UINT8)(Time.Year / 100);\r
+      Century = DecimalToBcd8 (Century);\r
+      DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));\r
+      RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);\r
+    }\r
+  }\r
+}\r