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 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 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 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 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 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 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