]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClock.c
EmbeddedPkg/RealTimeClockRuntimeDxe: move common functionality into core
[mirror_edk2.git] / EmbeddedPkg / RealTimeClockRuntimeDxe / RealTimeClock.c
index f1e067c0b59e31354f3ae59eaef629a187eec375..8323a4b4b84876aec4e42f68685a445a04719a99 100644 (file)
@@ -1,10 +1,8 @@
 /** @file\r
   Implement EFI RealTimeClock runtime services via RTC Lib.\r
 \r
-  Currently this driver does not support runtime virtual calling.\r
-\r
-\r
   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
+  Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
 **/\r
 \r
 #include <PiDxe.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/RealTimeClockLib.h>\r
 #include <Library/UefiLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
-#include <Library/RealTimeClockLib.h>\r
+#include <Library/UefiRuntimeLib.h>\r
 #include <Protocol/RealTimeClock.h>\r
 \r
 EFI_HANDLE  mHandle = NULL;\r
 \r
+//\r
+// These values can be set by SetTime () and need to be returned by GetTime ()\r
+// but cannot usually be kept by the RTC hardware, so we store them in a UEFI\r
+// variable instead.\r
+//\r
+typedef struct {\r
+  INT16           TimeZone;\r
+  UINT8           Daylight;\r
+} NON_VOLATILE_TIME_SETTINGS;\r
+\r
+STATIC CONST CHAR16 mTimeSettingsVariableName[] = L"RtcTimeSettings";\r
+STATIC NON_VOLATILE_TIME_SETTINGS mTimeSettings;\r
+\r
+STATIC\r
+BOOLEAN\r
+IsValidTimeZone (\r
+  IN  INT16  TimeZone\r
+  )\r
+{\r
+  return TimeZone == EFI_UNSPECIFIED_TIMEZONE ||\r
+         (TimeZone >= -1440 && TimeZone <= 1440);\r
+}\r
+\r
+STATIC\r
+BOOLEAN\r
+IsValidDaylight (\r
+  IN  INT8  Daylight\r
+  )\r
+{\r
+  return Daylight == 0 ||\r
+         Daylight == EFI_TIME_ADJUST_DAYLIGHT ||\r
+         Daylight == (EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT);\r
+}\r
 \r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsLeapYear (\r
+  IN EFI_TIME   *Time\r
+  )\r
+{\r
+  if (Time->Year % 4 == 0) {\r
+    if (Time->Year % 100 == 0) {\r
+      if (Time->Year % 400 == 0) {\r
+        return TRUE;\r
+      } else {\r
+        return FALSE;\r
+      }\r
+    } else {\r
+      return TRUE;\r
+    }\r
+  } else {\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+STATIC CONST INTN mDayOfMonth[12] = {\r
+  31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31\r
+};\r
+\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsDayValid (\r
+  IN  EFI_TIME  *Time\r
+  )\r
+{\r
+  ASSERT (Time->Day >= 1);\r
+  ASSERT (Time->Day <= mDayOfMonth[Time->Month - 1]);\r
+  ASSERT (Time->Month != 2 || IsLeapYear (Time) || Time->Day <= 28);\r
+\r
+  if (Time->Day < 1 ||\r
+      Time->Day > mDayOfMonth[Time->Month - 1] ||\r
+      (Time->Month == 2 && !IsLeapYear (Time) && Time->Day > 28)) {\r
+    return FALSE;\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsTimeValid(\r
+  IN EFI_TIME *Time\r
+  )\r
+{\r
+  // Check the input parameters are within the range specified by UEFI\r
+  if (Time->Year   < 1900               ||\r
+      Time->Year   > 9999               ||\r
+      Time->Month  < 1                  ||\r
+      Time->Month  > 12                 ||\r
+      !IsDayValid (Time)                ||\r
+      Time->Hour   > 23                 ||\r
+      Time->Minute > 59                 ||\r
+      Time->Second > 59                 ||\r
+      !IsValidTimeZone (Time->TimeZone) ||\r
+      !IsValidDaylight (Time->Daylight)) {\r
+    return FALSE;\r
+  }\r
+  return TRUE;\r
+}\r
 \r
 /**\r
   Returns the current time and date information, and the time-keeping capabilities\r
@@ -43,9 +143,20 @@ EFI_STATUS
 EFIAPI\r
 GetTime (\r
   OUT EFI_TIME                *Time,\r
-  OUT  EFI_TIME_CAPABILITIES  *Capabilities\r
+  OUT EFI_TIME_CAPABILITIES   *Capabilities\r
   )\r
 {\r
+  if (Time == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Set these first so the RealTimeClockLib implementation\r
+  // can override them based on its own settings.\r
+  //\r
+  Time->TimeZone = mTimeSettings.TimeZone;\r
+  Time->Daylight = mTimeSettings.Daylight;\r
+\r
   return LibGetTime (Time, Capabilities);\r
 }\r
 \r
@@ -67,7 +178,41 @@ SetTime (
   IN EFI_TIME                *Time\r
   )\r
 {\r
-  return LibSetTime (Time);\r
+  EFI_STATUS        Status;\r
+  BOOLEAN           TimeSettingsChanged;\r
+\r
+  if (Time == NULL || !IsTimeValid (Time)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  TimeSettingsChanged = FALSE;\r
+  if (mTimeSettings.TimeZone != Time->TimeZone ||\r
+      mTimeSettings.Daylight != Time->Daylight) {\r
+\r
+    mTimeSettings.TimeZone = Time->TimeZone;\r
+    mTimeSettings.Daylight = Time->Daylight;\r
+    TimeSettingsChanged = TRUE;\r
+  }\r
+\r
+  Status = LibSetTime (Time);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (TimeSettingsChanged) {\r
+    Status = EfiSetVariable (\r
+               (CHAR16 *)mTimeSettingsVariableName,\r
+               &gEfiCallerIdGuid,\r
+               EFI_VARIABLE_NON_VOLATILE |\r
+               EFI_VARIABLE_BOOTSERVICE_ACCESS |\r
+               EFI_VARIABLE_RUNTIME_ACCESS,\r
+               sizeof (mTimeSettings),\r
+               (VOID *)&mTimeSettings);\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 \r
@@ -138,12 +283,26 @@ InitializeRealTimeClock (
   )\r
 {\r
   EFI_STATUS  Status;\r
+  UINTN       Size;\r
 \r
   Status = LibRtcInitialize (ImageHandle, SystemTable);\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
+  Size = sizeof (mTimeSettings);\r
+  Status = EfiGetVariable ((CHAR16 *)mTimeSettingsVariableName,\r
+             &gEfiCallerIdGuid, NULL, &Size, (VOID *)&mTimeSettings);\r
+  if (EFI_ERROR (Status) ||\r
+      !IsValidTimeZone (mTimeSettings.TimeZone) ||\r
+      !IsValidDaylight (mTimeSettings.Daylight)) {\r
+    DEBUG ((DEBUG_WARN, "%a: using default timezone/daylight settings\n",\r
+      __FUNCTION__));\r
+\r
+    mTimeSettings.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
+    mTimeSettings.Daylight = 0;\r
+  }\r
+\r
   SystemTable->RuntimeServices->GetTime       = GetTime;\r
   SystemTable->RuntimeServices->SetTime       = SetTime;\r
   SystemTable->RuntimeServices->GetWakeupTime = GetWakeupTime;\r