1 /** @file
2   Implement EFI RealTimeClock runtime services via RTC Lib.
3 
4   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5   Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR>
6   Copyright (c) 2021, Ampere Computing LLC. All rights reserved.<BR>
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include <PiDxe.h>
13 #include <Library/DebugLib.h>
14 #include <Library/RealTimeClockLib.h>
15 #include <Library/TimeBaseLib.h>
16 #include <Library/UefiLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/UefiRuntimeLib.h>
19 #include <Protocol/RealTimeClock.h>
20 
21 EFI_HANDLE  mHandle = NULL;
22 
23 //
24 // These values can be set by SetTime () and need to be returned by GetTime ()
25 // but cannot usually be kept by the RTC hardware, so we store them in a UEFI
26 // variable instead.
27 //
28 typedef struct {
29   INT16           TimeZone;
30   UINT8           Daylight;
31 } NON_VOLATILE_TIME_SETTINGS;
32 
33 STATIC CONST CHAR16 mTimeSettingsVariableName[] = L"RtcTimeSettings";
34 STATIC NON_VOLATILE_TIME_SETTINGS mTimeSettings;
35 
36 /**
37   Returns the current time and date information, and the time-keeping capabilities
38   of the hardware platform.
39 
40   @param  Time                  A pointer to storage to receive a snapshot of the current time.
41   @param  Capabilities          An optional pointer to a buffer to receive the real time clock
42                                 device's capabilities.
43 
44   @retval EFI_SUCCESS           The operation completed successfully.
45   @retval EFI_INVALID_PARAMETER Time is NULL.
46   @retval EFI_DEVICE_ERROR      The time could not be retrieved due to hardware error.
47 
48 **/
49 EFI_STATUS
50 EFIAPI
GetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)51 GetTime (
52   OUT EFI_TIME                *Time,
53   OUT EFI_TIME_CAPABILITIES   *Capabilities
54   )
55 {
56   if (Time == NULL) {
57     return EFI_INVALID_PARAMETER;
58   }
59 
60   //
61   // Set these first so the RealTimeClockLib implementation
62   // can override them based on its own settings.
63   //
64   Time->TimeZone = mTimeSettings.TimeZone;
65   Time->Daylight = mTimeSettings.Daylight;
66 
67   return LibGetTime (Time, Capabilities);
68 }
69 
70 
71 
72 /**
73   Sets the current local time and date information.
74 
75   @param  Time                  A pointer to the current time.
76 
77   @retval EFI_SUCCESS           The operation completed successfully.
78   @retval EFI_INVALID_PARAMETER A time field is out of range.
79   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
80 
81 **/
82 EFI_STATUS
83 EFIAPI
SetTime(IN EFI_TIME * Time)84 SetTime (
85   IN EFI_TIME                *Time
86   )
87 {
88   EFI_STATUS        Status;
89   BOOLEAN           TimeSettingsChanged;
90 
91   if (Time == NULL || !IsTimeValid (Time)) {
92     return EFI_INVALID_PARAMETER;
93   }
94 
95   TimeSettingsChanged = FALSE;
96   if (mTimeSettings.TimeZone != Time->TimeZone ||
97       mTimeSettings.Daylight != Time->Daylight) {
98 
99     mTimeSettings.TimeZone = Time->TimeZone;
100     mTimeSettings.Daylight = Time->Daylight;
101     TimeSettingsChanged = TRUE;
102   }
103 
104   Status = LibSetTime (Time);
105   if (EFI_ERROR (Status)) {
106     return Status;
107   }
108 
109   if (TimeSettingsChanged) {
110     Status = EfiSetVariable (
111                (CHAR16 *)mTimeSettingsVariableName,
112                &gEfiCallerIdGuid,
113                EFI_VARIABLE_NON_VOLATILE |
114                EFI_VARIABLE_BOOTSERVICE_ACCESS |
115                EFI_VARIABLE_RUNTIME_ACCESS,
116                sizeof (mTimeSettings),
117                (VOID *)&mTimeSettings);
118     if (EFI_ERROR (Status)) {
119       return EFI_DEVICE_ERROR;
120     }
121   }
122   return EFI_SUCCESS;
123 }
124 
125 
126 /**
127   Returns the current wakeup alarm clock setting.
128 
129   @param  Enabled               Indicates if the alarm is currently enabled or disabled.
130   @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
131   @param  Time                  The current alarm setting.
132 
133   @retval EFI_SUCCESS           The alarm settings were returned.
134   @retval EFI_INVALID_PARAMETER Any parameter is NULL.
135   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
136 
137 **/
138 EFI_STATUS
139 EFIAPI
GetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)140 GetWakeupTime (
141   OUT BOOLEAN     *Enabled,
142   OUT BOOLEAN     *Pending,
143   OUT EFI_TIME    *Time
144   )
145 {
146   return LibGetWakeupTime (Enabled, Pending, Time);
147 }
148 
149 
150 /**
151   Sets the system wakeup alarm clock time.
152 
153   @param  Enabled               Enable or disable the wakeup alarm.
154   @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
155 
156   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
157                                 Enable is FALSE, then the wakeup alarm was disabled.
158   @retval EFI_INVALID_PARAMETER A time field is out of range.
159   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
160   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
161 
162 **/
163 EFI_STATUS
164 EFIAPI
SetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)165 SetWakeupTime (
166   IN BOOLEAN      Enabled,
167   OUT EFI_TIME    *Time
168   )
169 {
170   return LibSetWakeupTime (Enabled, Time);
171 }
172 
173 
174 
175 /**
176   This is the declaration of an EFI image entry point. This can be the entry point to an application
177   written to this specification, an EFI boot service driver, or an EFI runtime driver.
178 
179   @param  ImageHandle           Handle that identifies the loaded image.
180   @param  SystemTable           System Table for this image.
181 
182   @retval EFI_SUCCESS           The operation completed successfully.
183 
184 **/
185 EFI_STATUS
186 EFIAPI
InitializeRealTimeClock(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)187 InitializeRealTimeClock (
188   IN EFI_HANDLE                            ImageHandle,
189   IN EFI_SYSTEM_TABLE                      *SystemTable
190   )
191 {
192   EFI_STATUS  Status;
193   UINTN       Size;
194 
195   Status = LibRtcInitialize (ImageHandle, SystemTable);
196   if (EFI_ERROR (Status)) {
197     return Status;
198   }
199 
200   Size = sizeof (mTimeSettings);
201   Status = EfiGetVariable ((CHAR16 *)mTimeSettingsVariableName,
202              &gEfiCallerIdGuid, NULL, &Size, (VOID *)&mTimeSettings);
203   if (EFI_ERROR (Status) ||
204       !IsValidTimeZone (mTimeSettings.TimeZone) ||
205       !IsValidDaylight (mTimeSettings.Daylight)) {
206     DEBUG ((DEBUG_WARN, "%a: using default timezone/daylight settings\n",
207       __FUNCTION__));
208 
209     mTimeSettings.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
210     mTimeSettings.Daylight = 0;
211   }
212 
213   SystemTable->RuntimeServices->GetTime       = GetTime;
214   SystemTable->RuntimeServices->SetTime       = SetTime;
215   SystemTable->RuntimeServices->GetWakeupTime = GetWakeupTime;
216   SystemTable->RuntimeServices->SetWakeupTime = SetWakeupTime;
217 
218   Status = gBS->InstallMultipleProtocolInterfaces (
219                   &mHandle,
220                   &gEfiRealTimeClockArchProtocolGuid,
221                   NULL,
222                   NULL
223                   );
224 
225   return Status;
226 }
227 
228