1 /** @file
2 Implement EFI RealTimeClock runtime services via RTC Lib.
3
4 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
5 Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
6 Copyright (c) 2015, Hisilicon Limited. All rights reserved.<BR>
7 Copyright (c) 2015, Linaro Limited. All rights reserved.<BR>
8
9 This program and the accompanying materials
10 are licensed and made available under the terms and conditions of the BSD License
11 which accompanies this distribution. The full text of the license may be found at
12 http://opensource.org/licenses/bsd-license.php
13
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16
17 Based on the files under ArmPlatformPkg/Library/PL031RealTimeClockLib/PL031RealTimeClockLib.inf
18
19 **/
20
21 #include <Uefi.h>
22 #include <PiDxe.h>
23 #include <Library/BaseLib.h>
24 #include <Library/DebugLib.h>
25 #include <Library/UefiLib.h>
26 #include <Library/IoLib.h>
27 #include <Library/RealTimeClockLib.h>
28 #include <Library/MemoryAllocationLib.h>
29 #include <Library/PcdLib.h>
30 #include <Library/DxeServicesTableLib.h>
31 #include <Library/UefiBootServicesTableLib.h>
32 #include <Library/UefiRuntimeServicesTableLib.h>
33 #include <Library/UefiRuntimeLib.h>
34 #include <Library/EfiTimeBaseLib.h>
35
36 #include <Protocol/RealTimeClock.h>
37
38 #include <Guid/GlobalVariable.h>
39 #include <Guid/EventGroup.h>
40
41 #include <Library/ArmArchTimer.h>
42
43 STATIC CONST CHAR16 mTimeZoneVariableName[] = L"PV660VirtualRtcTimeZone";
44 STATIC CONST CHAR16 mDaylightVariableName[] = L"PV660VirtualRtcDaylight";
45 STATIC EFI_EVENT mRtcVirtualAddrChangeEvent;
46 STATIC EFI_RUNTIME_SERVICES *mRT;
47
48
49 STATIC INTN mEpochDiff = 0;
50
51 /**
52 Returns the current time and date information, and the time-keeping capabilities
53 of the hardware platform.
54
55 @param Time A pointer to storage to receive a snapshot of the current time.
56 @param Capabilities An optional pointer to a buffer to receive the real time clock
57 device's capabilities.
58
59 @retval EFI_SUCCESS The operation completed successfully.
60 @retval EFI_INVALID_PARAMETER Time is NULL.
61 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
62 @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
63 **/
64 EFI_STATUS
65 EFIAPI
LibGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)66 LibGetTime (
67 OUT EFI_TIME *Time,
68 OUT EFI_TIME_CAPABILITIES *Capabilities
69 )
70 {
71 EFI_STATUS Status = EFI_SUCCESS;
72 UINT64 Temp;
73 UINT32 EpochSeconds;
74 INT16 TimeZone = 0;
75 UINT8 Daylight = 0;
76 UINTN Size;
77
78 // Ensure Time is a valid pointer
79 if (Time == NULL) {
80 return EFI_INVALID_PARAMETER;
81 }
82
83 ArmArchTimerReadReg(CntPct,&Temp);
84
85 // UINT32 force convertion for PC-LINT
86 EpochSeconds = mEpochDiff + Temp / (UINT32) PcdGet32(PcdArmArchTimerFreqInHz);
87
88 // Get the current time zone information from non-volatile storage
89 Size = sizeof (TimeZone);
90 Status = mRT->GetVariable (
91 (CHAR16 *)mTimeZoneVariableName,
92 &gEfiCallerIdGuid,
93 NULL,
94 &Size,
95 (VOID *)&TimeZone
96 );
97
98 if (EFI_ERROR (Status)) {
99 ASSERT(Status != EFI_INVALID_PARAMETER);
100 ASSERT(Status != EFI_BUFFER_TOO_SMALL);
101
102 if (Status != EFI_NOT_FOUND)
103 goto EXIT;
104
105 // The time zone variable does not exist in non-volatile storage, so create it.
106 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
107 // Store it
108 Status = mRT->SetVariable (
109 (CHAR16 *)mTimeZoneVariableName,
110 &gEfiCallerIdGuid,
111 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
112 Size,
113 (VOID *)&(Time->TimeZone)
114 );
115 if (EFI_ERROR (Status)) {
116 DEBUG ((
117 EFI_D_ERROR,
118 "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
119 mTimeZoneVariableName,
120 Status
121 ));
122 goto EXIT;
123 }
124 } else {
125 // Got the time zone
126 Time->TimeZone = TimeZone;
127
128 // Check TimeZone bounds: -1440 to 1440 or 2047
129 if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440))
130 && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {
131 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
132 }
133
134 // Adjust for the correct time zone
135 if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
136 EpochSeconds += Time->TimeZone * SEC_PER_MIN;
137 }
138 }
139
140 // Get the current daylight information from non-volatile storage
141 Size = sizeof (Daylight);
142 Status = mRT->GetVariable (
143 (CHAR16 *)mDaylightVariableName,
144 &gEfiCallerIdGuid,
145 NULL,
146 &Size,
147 (VOID *)&Daylight
148 );
149
150 if (EFI_ERROR (Status)) {
151 ASSERT(Status != EFI_INVALID_PARAMETER);
152 ASSERT(Status != EFI_BUFFER_TOO_SMALL);
153
154 if (Status != EFI_NOT_FOUND)
155 goto EXIT;
156
157 // The daylight variable does not exist in non-volatile storage, so create it.
158 Time->Daylight = 0;
159 // Store it
160 Status = mRT->SetVariable (
161 (CHAR16 *)mDaylightVariableName,
162 &gEfiCallerIdGuid,
163 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
164 Size,
165 (VOID *)&(Time->Daylight)
166 );
167 if (EFI_ERROR (Status)) {
168 DEBUG ((
169 EFI_D_ERROR,
170 "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
171 mDaylightVariableName,
172 Status
173 ));
174 goto EXIT;
175 }
176 } else {
177 // Got the daylight information
178 Time->Daylight = Daylight;
179
180 // Adjust for the correct period
181 if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
182 // Convert to adjusted time, i.e. spring forwards one hour
183 EpochSeconds += SEC_PER_HOUR;
184 }
185 }
186
187 // Convert from internal 32-bit time to UEFI time
188 EpochToEfiTime (EpochSeconds, Time);
189
190 // Update the Capabilities info
191 if (Capabilities != NULL) {
192 Capabilities->Resolution = 1;
193 // Accuracy in ppm multiplied by 1,000,000, e.g. for 50ppm set 50,000,000
194
195 Capabilities->Accuracy = PcdGet32 (PcdArmArchTimerFreqInHz);
196 // FALSE: Setting the time does not clear the values below the resolution level
197 Capabilities->SetsToZero = FALSE;
198 }
199
200 EXIT:
201 return Status;
202 }
203
204
205 /**
206 Sets the current local time and date information.
207
208 @param Time A pointer to the current time.
209
210 @retval EFI_SUCCESS The operation completed successfully.
211 @retval EFI_INVALID_PARAMETER A time field is out of range.
212 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
213
214 **/
215 EFI_STATUS
216 EFIAPI
LibSetTime(IN EFI_TIME * Time)217 LibSetTime (
218 IN EFI_TIME *Time
219 )
220 {
221 EFI_STATUS Status;
222 UINTN EpochSeconds;
223 UINTN Temp;
224
225 // Check the input parameters are within the range specified by UEFI
226 if (!IsTimeValid (Time)) {
227 Status = EFI_INVALID_PARAMETER;
228 goto EXIT;
229 }
230
231 // Because the PL031 is a 32-bit counter counting seconds,
232 // the maximum time span is just over 136 years.
233 // Time is stored in Unix Epoch format, so it starts in 1970,
234 // Therefore it can not exceed the year 2106.
235 if ((Time->Year < 1970) || (Time->Year >= 2106)) {
236 Status = EFI_UNSUPPORTED;
237 goto EXIT;
238 }
239
240 EpochSeconds = EfiTimeToEpoch (Time);
241
242 // Adjust for the correct time zone, i.e. convert to UTC time zone
243 if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
244 EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
245 }
246
247 // TODO: Automatic Daylight activation
248
249 // Adjust for the correct period
250 if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
251 // Convert to un-adjusted time, i.e. fall back one hour
252 EpochSeconds -= SEC_PER_HOUR;
253 }
254
255 ArmArchTimerReadReg(CntPct,&Temp);
256
257 // UINT32 force convertion for PC-LINT
258 mEpochDiff = EpochSeconds - Temp / (UINT32) PcdGet32(PcdArmArchTimerFreqInHz);
259
260 // The accesses to Variable Services can be very slow, because we may be writing to Flash.
261 // Do this after having set the RTC.
262
263 // Save the current time zone information into non-volatile storage
264 Status = mRT->SetVariable (
265 (CHAR16 *)mTimeZoneVariableName,
266 &gEfiCallerIdGuid,
267 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
268 sizeof (Time->TimeZone),
269 (VOID *)&(Time->TimeZone)
270 );
271 if (EFI_ERROR (Status)) {
272 DEBUG ((
273 EFI_D_ERROR,
274 "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
275 mTimeZoneVariableName,
276 Status
277 ));
278 goto EXIT;
279 }
280
281 // Save the current daylight information into non-volatile storage
282 Status = mRT->SetVariable (
283 (CHAR16 *)mDaylightVariableName,
284 &gEfiCallerIdGuid,
285 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
286 sizeof(Time->Daylight),
287 (VOID *)&(Time->Daylight)
288 );
289 if (EFI_ERROR (Status)) {
290 DEBUG ((
291 EFI_D_ERROR,
292 "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",
293 mDaylightVariableName,
294 Status
295 ));
296 goto EXIT;
297 }
298
299 EXIT:
300 return Status;
301 }
302
303
304 /**
305 Returns the current wakeup alarm clock setting.
306
307 @param Enabled Indicates if the alarm is currently enabled or disabled.
308 @param Pending Indicates if the alarm signal is pending and requires acknowledgement.
309 @param Time The current alarm setting.
310
311 @retval EFI_SUCCESS The alarm settings were returned.
312 @retval EFI_INVALID_PARAMETER Any parameter is NULL.
313 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
314
315 **/
316 EFI_STATUS
317 EFIAPI
LibGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)318 LibGetWakeupTime (
319 OUT BOOLEAN *Enabled,
320 OUT BOOLEAN *Pending,
321 OUT EFI_TIME *Time
322 )
323 {
324 // Not a required feature
325 return EFI_UNSUPPORTED;
326 }
327
328
329 /**
330 Sets the system wakeup alarm clock time.
331
332 @param Enabled Enable or disable the wakeup alarm.
333 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
334
335 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If
336 Enable is FALSE, then the wakeup alarm was disabled.
337 @retval EFI_INVALID_PARAMETER A time field is out of range.
338 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
339 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
340
341 **/
342 EFI_STATUS
343 EFIAPI
LibSetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)344 LibSetWakeupTime (
345 IN BOOLEAN Enabled,
346 OUT EFI_TIME *Time
347 )
348 {
349 // Not a required feature
350 return EFI_UNSUPPORTED;
351 }
352
353 /**
354 Fixup internal data so that EFI can be call in virtual mode.
355 Call the passed in Child Notify event and convert any pointers in
356 lib to virtual mode.
357
358 @param[in] Event The Event that is being processed
359 @param[in] Context Event Context
360 **/
361 VOID
362 EFIAPI
LibRtcVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)363 LibRtcVirtualNotifyEvent (
364 IN EFI_EVENT Event,
365 IN VOID *Context
366 )
367 {
368 //
369 // Only needed if you are going to support the OS calling RTC functions in virtual mode.
370 // You will need to call EfiConvertPointer (). To convert any stored physical addresses
371 // to virtual address. After the OS transitions to calling in virtual mode, all future
372 // runtime calls will be made in virtual mode.
373 //
374 EfiConvertPointer (0x0, (VOID**)&mRT);
375 return;
376 }
377
378 /**
379 This is the declaration of an EFI image entry point. This can be the entry point to an application
380 written to this specification, an EFI boot service driver, or an EFI runtime driver.
381
382 @param ImageHandle Handle that identifies the loaded image.
383 @param SystemTable System Table for this image.
384
385 @retval EFI_SUCCESS The operation completed successfully.
386
387 **/
388 EFI_STATUS
389 EFIAPI
LibRtcInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)390 LibRtcInitialize (
391 IN EFI_HANDLE ImageHandle,
392 IN EFI_SYSTEM_TABLE *SystemTable
393 )
394 {
395 EFI_STATUS Status;
396 EFI_HANDLE Handle;
397
398 // Setup the setters and getters
399 gRT->GetTime = LibGetTime;
400 gRT->SetTime = LibSetTime;
401 gRT->GetWakeupTime = LibGetWakeupTime;
402 gRT->SetWakeupTime = LibSetWakeupTime;
403
404 mRT = gRT;
405
406 // Install the protocol
407 Handle = NULL;
408 Status = gBS->InstallMultipleProtocolInterfaces (
409 &Handle,
410 &gEfiRealTimeClockArchProtocolGuid, NULL,
411 NULL
412 );
413 ASSERT_EFI_ERROR (Status);
414
415 //
416 // Register for the virtual address change event
417 //
418 Status = gBS->CreateEventEx (
419 EVT_NOTIFY_SIGNAL,
420 TPL_NOTIFY,
421 LibRtcVirtualNotifyEvent,
422 NULL,
423 &gEfiEventVirtualAddressChangeGuid,
424 &mRtcVirtualAddrChangeEvent
425 );
426 ASSERT_EFI_ERROR (Status);
427
428 return Status;
429 }
430