• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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