/** @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
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
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
)\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