/** @file\r
Implement EFI RealTimeClock runtime services via RTC Lib.\r
\r
- Currently this driver does not support runtime virtual calling.\r
-\r
Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>\r
+ Copyright (c) 2011 - 2014, ARM 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
#include <Library/MemoryAllocationLib.h>\r
#include <Library/PcdLib.h>\r
#include <Library/ArmPlatformSysConfigLib.h>\r
+#include <Library/DxeServicesTableLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiRuntimeLib.h>\r
+\r
#include <Protocol/RealTimeClock.h>\r
+\r
#include <Guid/GlobalVariable.h>\r
+#include <Guid/EventGroup.h>\r
+\r
#include <Drivers/PL031RealTimeClock.h>\r
\r
#include <ArmPlatform.h>\r
\r
-CHAR16 mTimeZoneVariableName[] = L"PL031_TimeZone";\r
-CHAR16 mDaylightVariableName[] = L"PL031_Daylight";\r
-BOOLEAN mPL031Initialized = FALSE;\r
+STATIC CONST CHAR16 mTimeZoneVariableName[] = L"PL031RtcTimeZone";\r
+STATIC CONST CHAR16 mDaylightVariableName[] = L"PL031RtcDaylight";\r
+STATIC BOOLEAN mPL031Initialized = FALSE;\r
+STATIC EFI_EVENT mRtcVirtualAddrChangeEvent;\r
+STATIC UINTN mPL031RtcBase;\r
+STATIC EFI_RUNTIME_SERVICES *mRT;\r
\r
EFI_STATUS\r
IdentifyPL031 (\r
EFI_STATUS Status;\r
\r
// Check if this is a PrimeCell Peripheral\r
- if ( (MmioRead8 (PL031_RTC_PCELL_ID0) != 0x0D)\r
- || (MmioRead8 (PL031_RTC_PCELL_ID1) != 0xF0)\r
- || (MmioRead8 (PL031_RTC_PCELL_ID2) != 0x05)\r
- || (MmioRead8 (PL031_RTC_PCELL_ID3) != 0xB1)) {\r
+ if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID0) != 0x0D)\r
+ || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID1) != 0xF0)\r
+ || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID2) != 0x05)\r
+ || (MmioRead8 (mPL031RtcBase + PL031_RTC_PCELL_ID3) != 0xB1)) {\r
Status = EFI_NOT_FOUND;\r
goto EXIT;\r
}\r
\r
// Check if this PrimeCell Peripheral is the PL031 Real Time Clock\r
- if ( (MmioRead8 (PL031_RTC_PERIPH_ID0) != 0x31)\r
- || (MmioRead8 (PL031_RTC_PERIPH_ID1) != 0x10)\r
- || ((MmioRead8 (PL031_RTC_PERIPH_ID2) & 0xF) != 0x04)\r
- || (MmioRead8 (PL031_RTC_PERIPH_ID3) != 0x00)) {\r
+ if ( (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID0) != 0x31)\r
+ || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID1) != 0x10)\r
+ || ((MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID2) & 0xF) != 0x04)\r
+ || (MmioRead8 (mPL031RtcBase + PL031_RTC_PERIPH_ID3) != 0x00)) {\r
Status = EFI_NOT_FOUND;\r
goto EXIT;\r
}\r
}\r
\r
// Ensure interrupts are masked. We do not want RTC interrupts in UEFI\r
- if ((MmioRead32 (PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER) & PL031_SET_IRQ_MASK) != PL031_SET_IRQ_MASK) {\r
- MmioOr32 (PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER, PL031_SET_IRQ_MASK);\r
+ if ((MmioRead32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER) & PL031_SET_IRQ_MASK) != PL031_SET_IRQ_MASK) {\r
+ MmioOr32 (mPL031RtcBase + PL031_RTC_IMSC_IRQ_MASK_SET_CLEAR_REGISTER, PL031_SET_IRQ_MASK);\r
}\r
\r
// Clear any existing interrupts\r
- if ((MmioRead32 (PL031_RTC_RIS_RAW_IRQ_STATUS_REGISTER) & PL031_IRQ_TRIGGERED) == PL031_IRQ_TRIGGERED) {\r
- MmioOr32 (PL031_RTC_ICR_IRQ_CLEAR_REGISTER, PL031_CLEAR_IRQ);\r
+ if ((MmioRead32 (mPL031RtcBase + PL031_RTC_RIS_RAW_IRQ_STATUS_REGISTER) & PL031_IRQ_TRIGGERED) == PL031_IRQ_TRIGGERED) {\r
+ MmioOr32 (mPL031RtcBase + PL031_RTC_ICR_IRQ_CLEAR_REGISTER, PL031_CLEAR_IRQ);\r
}\r
\r
// Start the clock counter\r
- if ((MmioRead32 (PL031_RTC_CR_CONTROL_REGISTER) & PL031_RTC_ENABLED) != PL031_RTC_ENABLED) {\r
- MmioOr32 (PL031_RTC_CR_CONTROL_REGISTER, PL031_RTC_ENABLED);\r
+ if ((MmioRead32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER) & PL031_RTC_ENABLED) != PL031_RTC_ENABLED) {\r
+ MmioOr32 (mPL031RtcBase + PL031_RTC_CR_CONTROL_REGISTER, PL031_RTC_ENABLED);\r
}\r
\r
mPL031Initialized = TRUE;\r
UINTN ss;\r
UINTN J;\r
\r
- if (Time->Daylight == TRUE) {\r
-\r
- }\r
-\r
J = (EpochSeconds / 86400) + 2440588;\r
j = J + 32044;\r
g = j / 146097;\r
\r
JulianDate = Time->Day + ((153*m + 2)/5) + (365*y) + (y/4) - (y/100) + (y/400) - 32045;\r
\r
- ASSERT(JulianDate > EPOCH_JULIAN_DATE);\r
+ ASSERT (JulianDate >= EPOCH_JULIAN_DATE);\r
EpochDays = JulianDate - EPOCH_JULIAN_DATE;\r
\r
EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;\r
IN EFI_TIME *Time\r
)\r
{\r
- INTN DayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
+ STATIC CONST INTN DayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
\r
if (Time->Day < 1 ||\r
Time->Day > DayOfMonth[Time->Month - 1] ||\r
Returns the current time and date information, and the time-keeping capabilities\r
of the hardware platform.\r
\r
- @param Time A pointer to storage to receive a snapshot of the current time.\r
- @param Capabilities An optional pointer to a buffer to receive the real time clock\r
- device's capabilities.\r
+ @param Time A pointer to storage to receive a snapshot of the current time.\r
+ @param Capabilities An optional pointer to a buffer to receive the real time clock\r
+ device's capabilities.\r
\r
- @retval EFI_SUCCESS The operation completed successfully.\r
- @retval EFI_INVALID_PARAMETER Time is NULL.\r
- @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.\r
+ @retval EFI_SUCCESS The operation completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Time is NULL.\r
+ @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.\r
+ @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.\r
\r
**/\r
EFI_STATUS\r
)\r
{\r
EFI_STATUS Status = EFI_SUCCESS;\r
- UINTN EpochSeconds;\r
- INT16 *TimeZone = 0;\r
- UINTN *Daylight = 0;\r
+ UINT32 EpochSeconds;\r
+ INT16 TimeZone;\r
+ UINT8 Daylight;\r
+ UINTN Size;\r
\r
// Initialize the hardware if not already done\r
if (!mPL031Initialized) {\r
Status = ArmPlatformSysConfigGet (SYS_CFG_RTC, &EpochSeconds);\r
if (Status == EFI_UNSUPPORTED) {\r
// Battery backed up hardware RTC does not exist, revert to PL031\r
- EpochSeconds = MmioRead32 (PL031_RTC_DR_DATA_REGISTER);\r
+ EpochSeconds = MmioRead32 (mPL031RtcBase + PL031_RTC_DR_DATA_REGISTER);\r
Status = EFI_SUCCESS;\r
} else if (EFI_ERROR (Status)) {\r
// Battery backed up hardware RTC exists but could not be read due to error. Abort.\r
} else {\r
// Battery backed up hardware RTC exists and we read the time correctly from it.\r
// Now sync the PL031 to the new time.\r
- MmioWrite32 (PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);\r
+ MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);\r
}\r
\r
// Ensure Time is a valid pointer\r
}\r
\r
// Get the current time zone information from non-volatile storage\r
- TimeZone = (INT16 *)GetVariable(mTimeZoneVariableName, &gEfiGlobalVariableGuid);\r
+ Size = sizeof (TimeZone);\r
+ Status = mRT->GetVariable (\r
+ (CHAR16 *)mTimeZoneVariableName,\r
+ &gEfiCallerIdGuid,\r
+ NULL,\r
+ &Size,\r
+ (VOID *)&TimeZone\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT(Status != EFI_INVALID_PARAMETER);\r
+ ASSERT(Status != EFI_BUFFER_TOO_SMALL);\r
+\r
+ if (Status != EFI_NOT_FOUND)\r
+ goto EXIT;\r
\r
- if (TimeZone == NULL) {\r
// The time zone variable does not exist in non-volatile storage, so create it.\r
Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
// Store it\r
- Status = gRT->SetVariable (\r
- mTimeZoneVariableName,\r
- &gEfiGlobalVariableGuid,\r
- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
- sizeof(Time->TimeZone),\r
- &(Time->TimeZone)\r
- );\r
+ Status = mRT->SetVariable (\r
+ (CHAR16 *)mTimeZoneVariableName,\r
+ &gEfiCallerIdGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+ Size,\r
+ (VOID *)&(Time->TimeZone)\r
+ );\r
if (EFI_ERROR (Status)) {\r
- DEBUG((EFI_D_ERROR,"LibGetTime: ERROR: TimeZone\n"));\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",\r
+ mTimeZoneVariableName,\r
+ Status\r
+ ));\r
goto EXIT;\r
}\r
} else {\r
// Got the time zone\r
- Time->TimeZone = *TimeZone;\r
- FreePool(TimeZone);\r
+ Time->TimeZone = TimeZone;\r
\r
// Check TimeZone bounds: -1440 to 1440 or 2047\r
if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440))\r
}\r
\r
// Get the current daylight information from non-volatile storage\r
- Daylight = (UINTN *)GetVariable(mDaylightVariableName, &gEfiGlobalVariableGuid);\r
+ Size = sizeof (Daylight);\r
+ Status = mRT->GetVariable (\r
+ (CHAR16 *)mDaylightVariableName,\r
+ &gEfiCallerIdGuid,\r
+ NULL,\r
+ &Size,\r
+ (VOID *)&Daylight\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT(Status != EFI_INVALID_PARAMETER);\r
+ ASSERT(Status != EFI_BUFFER_TOO_SMALL);\r
+\r
+ if (Status != EFI_NOT_FOUND)\r
+ goto EXIT;\r
\r
- if (Daylight == NULL) {\r
// The daylight variable does not exist in non-volatile storage, so create it.\r
Time->Daylight = 0;\r
// Store it\r
- Status = gRT->SetVariable (\r
- mDaylightVariableName,\r
- &gEfiGlobalVariableGuid,\r
- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
- sizeof(Time->Daylight),\r
- &(Time->Daylight)\r
- );\r
+ Status = mRT->SetVariable (\r
+ (CHAR16 *)mDaylightVariableName,\r
+ &gEfiCallerIdGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+ Size,\r
+ (VOID *)&(Time->Daylight)\r
+ );\r
if (EFI_ERROR (Status)) {\r
- DEBUG((EFI_D_ERROR,"LibGetTime: ERROR: Daylight\n"));\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "LibGetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",\r
+ mDaylightVariableName,\r
+ Status\r
+ ));\r
goto EXIT;\r
}\r
} else {\r
// Got the daylight information\r
- Time->Daylight = *Daylight;\r
- FreePool(Daylight);\r
+ Time->Daylight = Daylight;\r
\r
// Adjust for the correct period\r
if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {\r
EFI_STATUS Status;\r
UINTN EpochSeconds;\r
\r
- // Because the PL031 is a 32-bit counter counting seconds,\r
- // the maximum time span is just over 136 years.\r
- // Time is stored in Unix Epoch format, so it starts in 1970,\r
- // Therefore it can not exceed the year 2106.\r
- // This is not a problem for UEFI, as the current spec limits the years\r
- // to the range 1998 .. 2011\r
-\r
- // Check the input parameters' range.\r
- if ((Time->Year < 1998) ||\r
- (Time->Year > 2099) ||\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
(!DayValid (Time) ) ||\r
goto EXIT;\r
}\r
\r
+ // Because the PL031 is a 32-bit counter counting seconds,\r
+ // the maximum time span is just over 136 years.\r
+ // Time is stored in Unix Epoch format, so it starts in 1970,\r
+ // Therefore it can not exceed the year 2106.\r
+ if ((Time->Year < 1970) || (Time->Year >= 2106)) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto EXIT;\r
+ }\r
+\r
// Initialize the hardware if not already done\r
if (!mPL031Initialized) {\r
Status = InitializePL031 ();\r
\r
\r
// Set the PL031\r
- MmioWrite32 (PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);\r
+ MmioWrite32 (mPL031RtcBase + PL031_RTC_LR_LOAD_REGISTER, EpochSeconds);\r
\r
// The accesses to Variable Services can be very slow, because we may be writing to Flash.\r
// Do this after having set the RTC.\r
\r
// Save the current time zone information into non-volatile storage\r
- Status = gRT->SetVariable (\r
- mTimeZoneVariableName,\r
- &gEfiGlobalVariableGuid,\r
- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
- sizeof(Time->TimeZone),\r
- &(Time->TimeZone)\r
- );\r
+ Status = mRT->SetVariable (\r
+ (CHAR16 *)mTimeZoneVariableName,\r
+ &gEfiCallerIdGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+ sizeof (Time->TimeZone),\r
+ (VOID *)&(Time->TimeZone)\r
+ );\r
if (EFI_ERROR (Status)) {\r
- DEBUG((EFI_D_ERROR,"LibSetTime: ERROR: TimeZone\n"));\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",\r
+ mTimeZoneVariableName,\r
+ Status\r
+ ));\r
goto EXIT;\r
}\r
\r
// Save the current daylight information into non-volatile storage\r
- Status = gRT->SetVariable (\r
- mDaylightVariableName,\r
- &gEfiGlobalVariableGuid,\r
- EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
- sizeof(Time->Daylight),\r
- &(Time->Daylight)\r
- );\r
+ Status = mRT->SetVariable (\r
+ (CHAR16 *)mDaylightVariableName,\r
+ &gEfiCallerIdGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+ sizeof(Time->Daylight),\r
+ (VOID *)&(Time->Daylight)\r
+ );\r
if (EFI_ERROR (Status)) {\r
- DEBUG((EFI_D_ERROR,"LibSetTime: ERROR: Daylight\n"));\r
+ DEBUG ((\r
+ EFI_D_ERROR,\r
+ "LibSetTime: Failed to save %s variable to non-volatile storage, Status = %r\n",\r
+ mDaylightVariableName,\r
+ Status\r
+ ));\r
goto EXIT;\r
}\r
\r
return EFI_UNSUPPORTED;\r
}\r
\r
+/**\r
+ Fixup internal data so that EFI can be call in virtual mode.\r
+ Call the passed in Child Notify event and convert any pointers in\r
+ lib to virtual mode.\r
\r
+ @param[in] Event The Event that is being processed\r
+ @param[in] Context Event Context\r
+**/\r
+VOID\r
+EFIAPI\r
+LibRtcVirtualNotifyEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // Only needed if you are going to support the OS calling RTC functions in virtual mode.\r
+ // You will need to call EfiConvertPointer (). To convert any stored physical addresses\r
+ // to virtual address. After the OS transitions to calling in virtual mode, all future\r
+ // runtime calls will be made in virtual mode.\r
+ //\r
+ EfiConvertPointer (0x0, (VOID**)&mPL031RtcBase);\r
+ EfiConvertPointer (0x0, (VOID**)&mRT);\r
+ return;\r
+}\r
\r
/**\r
This is the declaration of an EFI image entry point. This can be the entry point to an application\r
EFI_STATUS Status;\r
EFI_HANDLE Handle;\r
\r
+ // Initialize RTC Base Address\r
+ mPL031RtcBase = PcdGet32 (PcdPL031RtcBase);\r
+\r
+ // Declare the controller as EFI_MEMORY_RUNTIME\r
+ Status = gDS->AddMemorySpace (\r
+ EfiGcdMemoryTypeMemoryMappedIo,\r
+ mPL031RtcBase, SIZE_4KB,\r
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gDS->SetMemorySpaceAttributes (mPL031RtcBase, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
// Setup the setters and getters\r
gRT->GetTime = LibGetTime;\r
gRT->SetTime = LibSetTime;\r
gRT->GetWakeupTime = LibGetWakeupTime;\r
gRT->SetWakeupTime = LibSetWakeupTime;\r
\r
+ mRT = gRT;\r
+\r
// Install the protocol\r
Handle = NULL;\r
Status = gBS->InstallMultipleProtocolInterfaces (\r
&gEfiRealTimeClockArchProtocolGuid, NULL,\r
NULL\r
);\r
+ ASSERT_EFI_ERROR (Status);\r
\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- Fixup internal data so that EFI can be call in virtual mode.\r
- Call the passed in Child Notify event and convert any pointers in\r
- lib to virtual mode.\r
-\r
- @param[in] Event The Event that is being processed\r
- @param[in] Context Event Context\r
-**/\r
-VOID\r
-EFIAPI\r
-LibRtcVirtualNotifyEvent (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
- )\r
-{\r
//\r
- // Only needed if you are going to support the OS calling RTC functions in virtual mode.\r
- // You will need to call EfiConvertPointer (). To convert any stored physical addresses\r
- // to virtual address. After the OS transitions to calling in virtual mode, all future\r
- // runtime calls will be made in virtual mode.\r
+ // Register for the virtual address change event\r
//\r
- return;\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ LibRtcVirtualNotifyEvent,\r
+ NULL,\r
+ &gEfiEventVirtualAddressChangeGuid,\r
+ &mRtcVirtualAddrChangeEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
}\r