summaryrefslogtreecommitdiff
path: root/Silicon/Intel/LewisburgPkg/Library/DxeRuntimeResetSystemLib/PchReset.c
diff options
context:
space:
mode:
Diffstat (limited to 'Silicon/Intel/LewisburgPkg/Library/DxeRuntimeResetSystemLib/PchReset.c')
-rw-r--r--Silicon/Intel/LewisburgPkg/Library/DxeRuntimeResetSystemLib/PchReset.c598
1 files changed, 598 insertions, 0 deletions
diff --git a/Silicon/Intel/LewisburgPkg/Library/DxeRuntimeResetSystemLib/PchReset.c b/Silicon/Intel/LewisburgPkg/Library/DxeRuntimeResetSystemLib/PchReset.c
new file mode 100644
index 0000000000..cdc0f19c17
--- /dev/null
+++ b/Silicon/Intel/LewisburgPkg/Library/DxeRuntimeResetSystemLib/PchReset.c
@@ -0,0 +1,598 @@
+/** @file
+
+Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under
+the terms and conditions of the BSD License that accompanies this distribution.
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "PchReset.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED PCH_RESET_INSTANCE *mPchResetInstance;
+STATIC UINT8 mDaysOfMonthInfo[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+GLOBAL_REMOVE_IF_UNREFERENCED PCH_RESET_DATA mPchPowerCycleReset = {
+ PCH_PLATFORM_SPECIFIC_RESET_STRING,
+ PCH_POWER_CYCLE_RESET_GUID
+};
+GLOBAL_REMOVE_IF_UNREFERENCED PCH_RESET_DATA mPchGlobalReset = {
+ PCH_PLATFORM_SPECIFIC_RESET_STRING,
+ PCH_GLOBAL_RESET_GUID
+};
+GLOBAL_REMOVE_IF_UNREFERENCED PCH_RESET_DATA mPchGlobalResetWithEc = {
+ PCH_PLATFORM_SPECIFIC_RESET_STRING,
+ PCH_GLOBAL_RESET_WITH_EC_GUID
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mCapsuleResetType = 0;
+
+/**
+ Check if it is leap year
+
+ @param[in] Year year to be check
+
+ @retval True year is leap year
+ @retval FALSE year is not a leap year
+**/
+BOOLEAN
+IsLeapYear (
+ IN UINT16 Year
+ )
+{
+ return (Year % 4 == 0) && ((Year % 100 != 0) || (Year % 400 == 0));
+}
+
+/**
+ Set System Wakeup Alarm.
+
+ @param[in] WakeAfter Time offset in seconds to wake from S3
+
+ @retval EFI_SUCCESS Timer started successfully
+**/
+STATIC
+EFI_STATUS
+SetSystemWakeupAlarm (
+ IN UINT32 WakeAfter
+ )
+{
+ EFI_STATUS Status;
+ EFI_TIME Time;
+ EFI_TIME_CAPABILITIES Capabilities;
+ UINT32 Reminder;
+ UINT16 ABase;
+ UINT8 DayOfMonth;
+
+ ///
+ /// For an instant wake 2 seconds is a safe value
+ ///
+ if (WakeAfter < 2) {
+ WakeAfter = 2;
+ }
+
+ Status = EfiGetTime (&Time, &Capabilities);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Reminder = WakeAfter + (UINT32) Time.Second;
+ Time.Second = Reminder % 60;
+ Reminder = Reminder / 60;
+ Reminder = Reminder + (UINT32) Time.Minute;
+ Time.Minute = Reminder % 60;
+ Reminder = Reminder / 60;
+ Reminder = Reminder + (UINT32) Time.Hour;
+ Time.Hour = Reminder % 24;
+ Reminder = Reminder / 24;
+
+ if (Reminder > 0) {
+ Reminder = Reminder + (UINT32) Time.Day;
+ if ((Time.Month == 2) && IsLeapYear (Time.Year)) {
+ DayOfMonth = 29;
+ } else {
+ DayOfMonth = mDaysOfMonthInfo[Time.Month - 1];
+ }
+ if (Reminder > DayOfMonth) {
+ Time.Day = (UINT8)Reminder - DayOfMonth;
+ Reminder = 1;
+ } else {
+ Time.Day = (UINT8)Reminder;
+ Reminder = 0;
+ }
+ }
+ if (Reminder > 0) {
+ if (Time.Month == 12) {
+ Time.Month = 1;
+ Time.Year = Time.Year + 1;
+ } else {
+ Time.Month = Time.Month + 1;
+ }
+ }
+
+ Status = EfiSetWakeupTime (TRUE, &Time);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ABase = mPchResetInstance->PchAcpiBase;
+
+
+ ///
+ /// Clear RTC PM1 status
+ ///
+ IoWrite16 (ABase + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_RTC);
+
+ ///
+ /// set RTC_EN bit in PM1_EN to wake up from the alarm
+ ///
+ IoWrite16 (
+ ABase + R_PCH_ACPI_PM1_EN,
+ (IoRead16 (ABase + R_PCH_ACPI_PM1_EN) | B_PCH_ACPI_PM1_EN_RTC)
+ );
+ return Status;
+}
+
+/**
+ Retrieve PCH platform specific ResetData
+
+ @param[in] Guid PCH platform specific reset GUID.
+ @param[out] DataSize The size of ResetData in bytes.
+
+ @retval ResetData A platform specific reset that the exact type of
+ the reset is defined by the EFI_GUID that follows
+ the Null-terminated Unicode string.
+ @retval NULL If Guid is not defined in PCH platform specific reset.
+**/
+VOID *
+EFIAPI
+GetResetData (
+ IN EFI_GUID *Guid,
+ OUT UINTN *DataSize
+ )
+{
+ *DataSize = 0;
+ if (CompareGuid (Guid, &gPchPowerCycleResetGuid)) {
+ *DataSize = sizeof (mPchPowerCycleReset);
+ return (VOID *)&mPchPowerCycleReset;
+ } else if (CompareGuid (Guid, &gPchGlobalResetGuid)) {
+ *DataSize = sizeof (mPchGlobalReset);
+ return (VOID *)&mPchGlobalReset;
+ } else if (CompareGuid (Guid, &gPchGlobalResetWithEcGuid)) {
+ *DataSize = sizeof (mPchGlobalResetWithEc);
+ return (VOID *)&mPchGlobalResetWithEc;
+ }
+
+ return NULL;
+}
+
+/**
+ Execute Pch Reset from the host controller.
+
+ @param[in] This Pointer to the PCH_RESET_PROTOCOL instance.
+ @param[in] ResetType UEFI defined reset type.
+ @param[in] DataSize The size of ResetData in bytes.
+ @param[in] ResetData Optional element used to introduce a platform specific reset.
+ The exact type of the reset is defined by the EFI_GUID that follows
+ the Null-terminated Unicode string.
+
+ @retval EFI_SUCCESS Successfully completed.
+ @retval EFI_INVALID_PARAMETER If ResetType is invalid.
+**/
+EFI_STATUS
+EFIAPI
+Reset (
+ IN PCH_RESET_PROTOCOL *This,
+ IN EFI_RESET_TYPE ResetType,
+ IN UINTN DataSize,
+ IN VOID *ResetData OPTIONAL
+ )
+{
+ PCH_RESET_INSTANCE *PchResetInstance;
+ EFI_STATUS Status;
+ PCH_RESET_TYPE PchResetType;
+
+ PchResetInstance = PCH_RESET_INSTANCE_FROM_THIS (This);
+ PchResetType = (PCH_RESET_TYPE)ResetType;
+
+ Status = PchReset (PchResetInstance, PchResetType);
+
+ return Status;
+}
+
+/**
+ Calling this function causes a system-wide reset. This sets
+ all circuitry within the system to its initial state. This type of reset
+ is asynchronous to system operation and operates without regard to
+ cycle boundaries.
+
+ System reset should not return, if it returns, it means the system does
+ not support cold reset.
+**/
+VOID
+EFIAPI
+ResetCold (
+ VOID
+ )
+{
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) EfiResetCold);
+}
+
+/**
+ Calling this function causes a system-wide initialization. The processors
+ are set to their initial state, and pending cycles are not corrupted.
+
+ System reset should not return, if it returns, it means the system does
+ not support warm reset.
+**/
+VOID
+EFIAPI
+ResetWarm (
+ VOID
+ )
+{
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) EfiResetWarm);
+}
+
+/**
+ Calling this function causes the system to enter a power state equivalent
+ to the ACPI G2/S5 or G3 states.
+
+ System shutdown should not return, if it returns, it means the system does
+ not support shut down reset.
+**/
+VOID
+EFIAPI
+ResetShutdown (
+ VOID
+ )
+{
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) EfiResetShutdown);
+}
+
+/**
+ Calling this function causes the system to enter a power state for platform specific.
+
+ @param[in] ResetStatus The status code for the reset.
+ @param[in] DataSize The size of ResetData in bytes.
+ @param[in] ResetData Optional element used to introduce a platform specific reset.
+ The exact type of the reset is defined by the EFI_GUID that follows
+ the Null-terminated Unicode string.
+
+**/
+VOID
+EFIAPI
+ResetPlatformSpecific (
+ IN EFI_STATUS ResetStatus,
+ IN UINTN DataSize,
+ IN VOID *ResetData OPTIONAL
+ )
+{
+ EFI_GUID *GuidPtr;
+
+ if (ResetData == NULL) {
+ if (!EfiAtRuntime ()) {
+ DEBUG ((DEBUG_ERROR, "[DxeRuntimeResetSystemLib] ResetData is not available.\n"));
+ }
+ return;
+ }
+ GuidPtr = (EFI_GUID *) ((UINT8 *) ResetData + DataSize - sizeof (EFI_GUID));
+ if (CompareGuid (GuidPtr, &gPchPowerCycleResetGuid)) {
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) PowerCycleReset);
+ } else if (CompareGuid (GuidPtr, &gPchGlobalResetGuid)) {
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) GlobalReset);
+ } else if (CompareGuid (GuidPtr, &gPchGlobalResetWithEcGuid)) {
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) GlobalResetWithEc);
+ } else {
+ return;
+ }
+}
+
+/**
+ Calling this function causes the system to enter a power state for capsule update.
+
+ Reset update should not return, if it returns, it means the system does
+ not support capsule update.
+
+**/
+VOID
+EFIAPI
+EnterS3WithImmediateWake (
+ VOID
+ )
+{
+ PchReset (mPchResetInstance, (PCH_RESET_TYPE) EfiResetWarm);
+}
+
+/**
+ <b>PchReset Runtime DXE Driver Entry Point</b>\n
+ - <b>Introduction</b>\n
+ The PchReset Runtime DXE driver provide a standard way for other modules to
+ use the PCH Reset Interface in DXE/SMM/Runtime environments. It has no longer
+ hooked ResetSystem() function of the runtime service table.
+
+ - @pre
+ - If there is any driver which needs to run the callback function right before
+ issuing the reset, PCH Reset Callback Protocol will need to be installed
+ before PCH Reset Runtime DXE driver. If PchReset Runtime DXE driver is run
+ before Status Code Runtime Protocol is installed and there is the need
+ to use Status code in the driver, it will be necessary to add EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID
+ to the dependency file.
+ - @link _PCH_RESET_CALLBACK_PROTOCOL PCH_RESET_CALLBACK_PROTOCOL @endlink
+
+ - @result
+ The Reset driver produces @link _PCH_RESET_PROTOCOL PCH_RESET_PROTOCOL @endlink
+
+ @param[in] ImageHandle Image handle of the loaded driver
+ @param[in] SystemTable Pointer to the System Table
+
+ @retval EFI_SUCCESS Thread can be successfully created
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
+ @retval EFI_DEVICE_ERROR Cannot create the timer service
+**/
+EFI_STATUS
+EFIAPI
+InstallPchReset (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINT64 BaseAddress;
+ UINT64 Length;
+ UINT32 PwrmBaseAddress;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemorySpaceDescriptor;
+ UINT64 Attributes;
+ EFI_EVENT AddressChangeEvent;
+ EFI_PEI_HOB_POINTERS HobPtr;
+ PCH_POLICY_HOB *PchPolicyHob;
+
+ DEBUG ((DEBUG_INFO, "InstallPchReset() Start\n"));
+
+ //
+ // Set PMC PCI address space to RUNTIME MEMORY.
+ //
+ BaseAddress = MmPciBase(
+ DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_PMC,
+ PCI_FUNCTION_NUMBER_PCH_PMC
+ );
+ Length = 0x1000; // 4KB
+
+ Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &MemorySpaceDescriptor);
+ ASSERT_EFI_ERROR (Status);
+
+ Attributes = MemorySpaceDescriptor.Attributes | EFI_MEMORY_RUNTIME;
+
+ Status = gDS->SetMemorySpaceAttributes (
+ BaseAddress,
+ Length,
+ Attributes
+ );
+ ASSERT_EFI_ERROR (Status);
+ //
+ // Set PWRM MMIO address space to RUNTIME MEMORY.
+ //
+ PchPwrmBaseGet (&PwrmBaseAddress);
+ Length = 0x10000; // 64KB
+
+ Status = gDS->GetMemorySpaceDescriptor (PwrmBaseAddress, &MemorySpaceDescriptor);
+ ASSERT_EFI_ERROR (Status);
+
+ Attributes = MemorySpaceDescriptor.Attributes | EFI_MEMORY_RUNTIME;
+
+ Status = gDS->SetMemorySpaceAttributes (
+ PwrmBaseAddress,
+ Length,
+ Attributes
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ ///
+ /// Allocate Runtime memory for the PchReset protocol instance.
+ ///
+ mPchResetInstance = AllocateRuntimeZeroPool (sizeof (PCH_RESET_INSTANCE));
+ if (mPchResetInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = PchResetConstructor (mPchResetInstance);
+
+ ///
+ /// Create Address Change event
+ ///
+ ///
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PchResetVirtualAddressChangeEvent,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &AddressChangeEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ HobPtr.Guid = GetFirstGuidHob (&gPchPolicyHobGuid);
+ if (HobPtr.Guid != NULL) {
+ PchPolicyHob = GET_GUID_HOB_DATA (HobPtr.Guid);
+ mCapsuleResetType = PchPolicyHob->PmConfig.CapsuleResetType;
+ }
+ ///
+ /// The Lib Deconstruct will automatically be called when entrypoint return error.
+ ///
+ DEBUG ((DEBUG_INFO, "InstallPchReset() End\n"));
+
+ return Status;
+}
+
+/**
+ If need be, do any special reset required for capsules. For this
+ implementation where we're called from the ResetSystem() api,
+ just set our capsule variable and return to let the caller
+ do a soft reset.
+**/
+VOID
+CapsuleS3Reset (
+ VOID
+ )
+{
+ UINT32 Data32;
+ UINT32 Eflags;
+ UINT16 ABase;
+
+ DEBUG ((DEBUG_INFO, "Capsule Present: Will be issuing S3 reset.\n"));
+
+ ///
+ /// Wake up system 2 seconds after putting system into S3 to complete the reset operation.
+ ///
+ SetSystemWakeupAlarm (2);
+ ///
+ /// Process capsules across a system reset.
+ ///
+ ABase = mPchResetInstance->PchAcpiBase;
+ ASSERT (ABase != 0);
+
+ Data32 = IoRead32 ((UINTN) (ABase + R_PCH_ACPI_PM1_CNT));
+
+ Data32 = (UINT32) ((Data32 & ~(B_PCH_ACPI_PM1_CNT_SLP_TYP + B_PCH_ACPI_PM1_CNT_SLP_EN)) | V_PCH_ACPI_PM1_CNT_S3);
+
+ Eflags = (UINT32) AsmReadEflags ();
+
+ if ((Eflags & 0x200)) {
+ DisableInterrupts ();
+ }
+
+ AsmWbinvd ();
+ AsmWriteCr0 (AsmReadCr0 () | 0x060000000);
+
+ IoWrite32 (
+ (UINTN) (ABase + R_PCH_ACPI_PM1_CNT),
+ (UINT32) Data32
+ );
+
+ Data32 = Data32 | B_PCH_ACPI_PM1_CNT_SLP_EN;
+
+ IoWrite32 (
+ (UINTN) (ABase + R_PCH_ACPI_PM1_CNT),
+ (UINT32) Data32
+ );
+
+ if ((Eflags & 0x200)) {
+ EnableInterrupts ();
+ }
+ ///
+ /// Should not return
+ ///
+ CpuDeadLoop ();
+}
+
+/**
+ Execute call back function for Pch Reset.
+
+ @param[in] PchResetType Pch Reset Types which includes PowerCycle, Globalreset.
+
+ @retval EFI_SUCCESS The callback function has been done successfully
+ @retval EFI_NOT_FOUND Failed to find Pch Reset Callback protocol. Or, none of
+ callback protocol is installed.
+ @retval Others Do not do any reset from PCH
+**/
+EFI_STATUS
+EFIAPI
+PchResetCallback (
+ IN PCH_RESET_TYPE PchResetType
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumHandles;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+ PCH_RESET_CALLBACK_PROTOCOL *PchResetCallback;
+ UINTN Size;
+ UINTN CapsuleDataPtr;
+
+ if (EfiAtRuntime () == FALSE) {
+ DEBUG((DEBUG_ERROR, "Not in Runtime"));
+ ///
+ /// Retrieve all instances of Pch Reset Callback protocol
+ ///
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gPchResetCallbackProtocolGuid,
+ NULL,
+ &NumHandles,
+ &HandleBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ ///
+ /// Those drivers that need to install Pch Reset Callback protocol have the responsibility
+ /// to make sure themselves execute before Pch Reset Runtime driver.
+ ///
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_ERROR | DEBUG_INFO, "Or, none of Pch Reset callback protocol is installed.\n"));
+ }
+
+ return Status;
+ }
+
+ for (Index = 0; Index < NumHandles; Index++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gPchResetCallbackProtocolGuid,
+ (VOID **) &PchResetCallback
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ if (!EFI_ERROR (Status)) {
+ DEBUG((EFI_D_ERROR, "Calling PchResetCallback %d\n", Index));
+ PchResetCallback->ResetCallback (PchResetType);
+ } else {
+ DEBUG ((DEBUG_ERROR | DEBUG_INFO, "Failed to locate Pch Reset Callback protocol.\n"));
+ return Status;
+ }
+ }
+ }
+ DEBUG((EFI_D_ERROR, "PchResetCallback After Runtime Check\n"));
+ if(PchResetType == WarmReset) {
+ ///
+ /// Check if there are pending capsules to process
+ ///
+ DEBUG((EFI_D_ERROR, "PchResetCallback Warmreset\n"));
+ Size = sizeof (CapsuleDataPtr);
+ Status = EfiGetVariable (
+ EFI_CAPSULE_VARIABLE_NAME,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ (VOID *) &CapsuleDataPtr
+ );
+ if (Status == EFI_SUCCESS) {
+ if (mCapsuleResetType == CAPSULE_RESET_S3) { //default value S3 resume
+ CapsuleS3Reset ();
+ }
+ AsmWbinvd ();
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Fixup internal data pointers so that the services can be called in virtual mode.
+
+ @param[in] Event The event registered.
+ @param[in] Context Event context. Not used in this event handler.
+
+**/
+VOID
+EFIAPI
+PchResetVirtualAddressChangeEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mPchResetInstance->PchPmcBase));
+ gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mPchResetInstance->PchPwrmBase));
+ gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mPchResetInstance));
+}