PcAtChipsetPkg/PcRtc: Fix a Y2K bug
authorRuiyu Ni <ruiyu.ni@intel.com>
Thu, 11 Jun 2015 07:14:18 +0000 (07:14 +0000)
committerniruiyu <niruiyu@Edk2>
Thu, 11 Jun 2015 07:14:18 +0000 (07:14 +0000)
The original driver cannot handle the case when system time runs from 1999/12/31 23:59:59
to 2000/1/1 0:0:0.
A simple test to set system time to 1999/12/31 23:59:59 can expose this bug.
The patch limits the driver to only support year in 100 range and decide the century value based
on the supporting range: Century either equals to PcdMinimalYear / 100 or equals to PcdMinimalYear / 100 + 1.

The patch passed the Y2K test.
However with year range [1998, 2097], when system time is 2097/12/31 23:59:59,
the next second system time will become 1998/1/1 0:0:0. I think it's a acceptable limitation.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17624 6f19259b-4bc3-4df7-8a09-765794883524

PcAtChipsetPkg/PcAtChipsetPkg.dec
PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.h

index fb6fb8b..6a1415c 100644 (file)
 \r
   ## This PCD specifies the maximal valid year in RTC.\r
   # @Prompt Maximal valid year in RTC.\r
-  gPcAtChipsetPkgTokenSpaceGuid.PcdMaximalValidYear|2099|UINT16|0x0000000E\r
+  # @Expression gPcAtChipsetPkgTokenSpaceGuid.PcdMaximalValidYear < gPcAtChipsetPkgTokenSpaceGuid.PcdMinimalValidYear + 100\r
+  gPcAtChipsetPkgTokenSpaceGuid.PcdMaximalValidYear|2097|UINT16|0x0000000E\r
   \r
 [PcdsFixedAtBuild, PcdsPatchableInModule]\r
   ## Defines the ACPI register set base address.\r
   gPcAtChipsetPkgTokenSpaceGuid.PcdAcpiIoPortBaseAddressMask     |0xFFFE|UINT16|0x00000018\r
 \r
 [UserExtensions.TianoCore."ExtraFiles"]\r
-  PcAtChipsetPkgExtra.uni
\ No newline at end of file
+  PcAtChipsetPkgExtra.uni\r
index 23f8d3b..9ec309c 100644 (file)
@@ -100,7 +100,6 @@ 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
@@ -163,8 +162,6 @@ 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
@@ -201,7 +198,7 @@ PcRtcInit (
   //\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
@@ -218,7 +215,7 @@ PcRtcInit (
     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.Year   = PcdGet16 (PcdMinimalValidYear);\r
     Time.Nanosecond  = 0;\r
     Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
     Time.Daylight = 0;\r
@@ -251,7 +248,7 @@ PcRtcInit (
   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.Year   = PcdGet16 (PcdMinimalValidYear);\r
   Time.Nanosecond  = 0;\r
   Time.TimeZone = Global->SavedTimeZone;\r
   Time.Daylight = Global->Daylight;;\r
@@ -272,8 +269,8 @@ PcRtcInit (
     }\r
     return EFI_DEVICE_ERROR;\r
   }\r
-  \r
-  ConvertEfiTimeToRtcTime (&Time, RegisterB, &Century);\r
+\r
+  ConvertEfiTimeToRtcTime (&Time, RegisterB);\r
 \r
   //\r
   // Set the Y/M/D info to variable as it has no corresponding hw registers.\r
@@ -343,7 +340,6 @@ PcRtcGetTime (
 {\r
   EFI_STATUS      Status;\r
   RTC_REGISTER_B  RegisterB;\r
-  UINT8           Century;\r
 \r
   //\r
   // Check parameters for null pointer\r
@@ -383,8 +379,6 @@ PcRtcGetTime (
   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
@@ -401,7 +395,7 @@ PcRtcGetTime (
   //\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
@@ -447,7 +441,6 @@ PcRtcSetTime (
   EFI_STATUS      Status;\r
   EFI_TIME        RtcTime;\r
   RTC_REGISTER_B  RegisterB;\r
-  UINT8           Century;\r
   UINT32          TimerVar;\r
 \r
   if (Time == NULL) {\r
@@ -506,7 +499,7 @@ PcRtcSetTime (
   RegisterB.Bits.Set  = 1;\r
   RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
 \r
-  ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
+  ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
 \r
   RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r
   RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);\r
@@ -514,7 +507,6 @@ PcRtcSetTime (
   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
 \r
   //\r
   // Allow updates of the RTC registers\r
@@ -564,7 +556,6 @@ 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
@@ -612,8 +603,6 @@ PcRtcGetWakeupTime (
   Time->TimeZone = Global->SavedTimeZone;\r
   Time->Daylight = Global->Daylight;\r
 \r
-  Century = RtcRead (RTC_ADDRESS_CENTURY);\r
-\r
   //\r
   // Get the alarm info from variable\r
   //\r
@@ -644,7 +633,7 @@ PcRtcGetWakeupTime (
   //\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
@@ -680,7 +669,6 @@ PcRtcSetWakeupTime (
   EFI_STATUS            Status;\r
   EFI_TIME              RtcTime;\r
   RTC_REGISTER_B        RegisterB;\r
-  UINT8                 Century;\r
   EFI_TIME_CAPABILITIES Capabilities;\r
 \r
   ZeroMem (&RtcTime, sizeof (RtcTime));\r
@@ -736,7 +724,7 @@ PcRtcSetWakeupTime (
   RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
 \r
   if (Enable) {\r
-    ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
+    ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
   } else {\r
     //\r
     // if the alarm is disable, record the current setting.\r
@@ -837,7 +825,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
@@ -848,11 +835,11 @@ 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
+  UINT8   Century;\r
 \r
   if ((Time->Hour & 0x80) != 0) {\r
     IsPM = TRUE;\r
@@ -870,14 +857,21 @@ ConvertRtcTimeToEfiTime (
     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
+      Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \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
   Time->Year = (UINT16) (Century * 100 + Time->Year);\r
 \r
   //\r
@@ -1053,14 +1047,11 @@ IsLeapYear (
   @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
@@ -1081,10 +1072,8 @@ ConvertEfiTimeToRtcTime (
     }\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
 \r
   if (RegisterB.Bits.Dm == 0) {\r
index 020d715..026c108 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Header file for real time clock driver.\r
 \r
-Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2015, 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
@@ -61,7 +61,6 @@ typedef struct {
 #define RTC_ADDRESS_REGISTER_B        11  // R/W\r
 #define RTC_ADDRESS_REGISTER_C        12  // RO\r
 #define RTC_ADDRESS_REGISTER_D        13  // RO\r
-#define RTC_ADDRESS_CENTURY           50  // R/W  Range 19..20 Bit 8 is R/W\r
 //\r
 // Date and time initial values.\r
 // They are used if the RTC values are invalid during driver initialization\r
@@ -71,7 +70,6 @@ typedef struct {
 #define RTC_INIT_HOUR   0\r
 #define RTC_INIT_DAY    1\r
 #define RTC_INIT_MONTH  1\r
-#define RTC_INIT_YEAR   2001\r
 \r
 //\r
 // Register initial values\r
@@ -287,14 +285,11 @@ RtcTimeFieldsValid (
   @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
 \r
@@ -308,7 +303,6 @@ ConvertEfiTimeToRtcTime (
 \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
@@ -319,7 +313,6 @@ ConvertEfiTimeToRtcTime (
 EFI_STATUS\r
 ConvertRtcTimeToEfiTime (\r
   IN OUT EFI_TIME        *Time,\r
-  IN     UINT8           Century,\r
   IN     RTC_REGISTER_B  RegisterB\r
   );\r
 \r