/** @file This Peim driver will do RapidStart Entry or RapidStart Exit transition @copyright Copyright (c) 1999 - 2013 Intel Corporation. All rights reserved This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. This file contains an 'Intel Peripheral Driver' and uniquely identified as "Intel Reference Module" and is licensed for Intel CPUs and chipsets under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to additional terms of the license agreement **/ // // External include files do NOT need to be explicitly specified in real EDKII // environment // #if !defined(EDK_RELEASE_VERSION) || (EDK_RELEASE_VERSION < 0x00020000) #include "EdkIIGluePeim.h" #include "RapidStartConfig.h" #include EFI_PPI_DEFINITION (RapidStart) #include EFI_PROTOCOL_DEFINITION (RapidStartGlobalNvsArea) #include EFI_PPI_DEFINITION (Heci) #include #include #include #include #include "GfxDisplayLibPei.h" #include EFI_PPI_DEFINITION (PeiGfxPpi) #ifdef RAPID_START_ON_MEMORY_INSTALLED #include EFI_GUID_DEFINITION (AcpiVariable) #endif #include EFI_PPI_DEFINITION (EndOfPeiSignal) #include EFI_GUID_DEFINITION (RapidStartTransition) #include "RapidStartPeiLib.h" #include "RapidStartData.h" #include "RapidStartAhci.h" #include "RapidStartCommonLib.h" #endif #include EFI_PPI_DEPENDENCY (SaPlatformPolicy) #define PAM_COUNT 7 #define ME_FID_TIMEOUT 10000 #define MAX_MEMORY_RANGES (MAX_GFX_MEMORY_RANGES + 0x08) #define MAX_SMRAM_RANGES 2 #define DPR_RANGES 1 #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #ifndef MIN #define MIN(a, b) (((a) <= (b)) ? (a) : (b)) #endif #define ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a))) #define POOL_SIZE(a) (((((EFI_HOB_GENERIC_HEADER*)a - 1)->HobLength) - sizeof(EFI_HOB_GENERIC_HEADER)) / sizeof (*(a))) #define VARIABLE_SIZE(a) (ARRAY_SIZE(a) > 0) ? ARRAY_SIZE(a) : POOL_SIZE(a) #define END_RANGES ((UINT64) (INT64) (-1)) #define RANGE_LENGTH(r) ((r)->End - (r)->Start) EFI_GUID gEfiAcpiVariableGuid = EFI_ACPI_VARIABLE_GUID; typedef struct { UINT64 Start; UINT64 End; } MEMORY_RANGE; #define RAPID_START_INSTANCE_SIGNATURE EFI_SIGNATURE_32 ('i', 'R', 'S', 'T') typedef struct _RAPID_START_INSTANCE { RAPID_START_PPI Ppi; EFI_PEI_PPI_DESCRIPTOR PpiDesc; UINT32 Signature; BOOLEAN Enabled; RAPID_START_PERSISTENT_DATA Data; RAPID_START_TRANSITION Transition; RAPID_START_GLOBAL_NVS_AREA *RapidStartGlobalNvs; AHCI_CONTEXT Ahci; CHAR8 HddPassword[ATA_PASSWORD_LEN]; EFI_STATUS PwdStatus; BOOLEAN FreezeLock; #ifndef RAPID_START_NO_SMRAM_INTEGRITY_CHECK UINT8 SmRamHash[RAPID_START_SECURE_HASH_LENGTH]; #endif UINT64 NotUsedLba; UINT32 IedSize; } RAPID_START_INSTANCE; #define RAPID_START_INSTANCE_FROM_THIS(a) CR (a, RAPID_START_INSTANCE, Ppi, RAPID_START_INSTANCE_SIGNATURE) EFI_STATUS RapidStartClearAndEnablePmeEvent ( IN BOOLEAN EnablePme ); STATIC EFI_STATUS RapidStartExitAtEndOfPei ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ); STATIC EFI_STATUS RapidStartRecoveryAtEndOfPei ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ); STATIC EFI_STATUS RapidStartS4AtEndOfPei ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ); STATIC EFI_STATUS RapidStartMemoryInstalled ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ); STATIC EFI_STATUS InstallRapidStartPpi ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ); STATIC EFI_PEI_NOTIFY_DESCRIPTOR mRapidStartExitNotifyDesc = { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEndOfPeiSignalPpiGuid, RapidStartExitAtEndOfPei }; STATIC EFI_PEI_NOTIFY_DESCRIPTOR mRapidStartRecoveryNotifyDesc = { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEndOfPeiSignalPpiGuid, RapidStartRecoveryAtEndOfPei }; STATIC EFI_PEI_NOTIFY_DESCRIPTOR mRapidStartS4NotifyDesc = { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEndOfPeiSignalPpiGuid, RapidStartS4AtEndOfPei }; STATIC EFI_PEI_NOTIFY_DESCRIPTOR mRapidStartTransitionPpiNotifyDesc = { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gRapidStartTransitionPpiGuid, RapidStartMemoryInstalled }; STATIC EFI_PEI_NOTIFY_DESCRIPTOR mInstallRapidStartPpiNotifyDesc = { (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gPeiHeciPpiGuid, InstallRapidStartPpi }; STATIC EFI_STATUS RapidStartDoTransition ( IN RAPID_START_PPI *This ); #if !defined(RAPID_START_NO_SMRAM_INTEGRITY_CHECK) && defined(BUILD_WITH_GLUELIB) #undef SetMem /** SetMem alternative in case GlueLib defines SetMem as a macro. Required by CryptoLib. @param[out] Buffer - Pointer to the memory buffer @param[in] Length - The length for setting memory @param[in] Value - The value that will be set to memory buffer **/ VOID * SetMem ( OUT VOID *Buffer, IN UINTN Length, IN UINT8 Value ) { return GlueSetMem (Buffer, Length, Value); } #undef CopyMem /** Copy Length bytes from Source to Destination. @param[out] DestinationBuffer - Target of copy @param[in] SourceBuffer - Place to copy from @param[in] Length - Number of bytes to copy @retval Status code **/ VOID * EFIAPI CopyMem ( OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length ) { return GlueCopyMem (DestinationBuffer, SourceBuffer, Length); } #endif #ifndef EFI_DEBUG #define PrintRanges(r) #else /** Show memory range information by debug macro @param[in] ranges - Memory range information structure **/ VOID PrintRanges ( IN MEMORY_RANGE *ranges ) { while (ranges->Start != END_RANGES) { DEBUG ((EFI_D_INFO, "%08lx - %08lx\n", ranges->Start, ranges->End)); ++ranges; } } #endif /** Sort memory ranges. @param[in][out] MemoryRanges - Memory Ranges @param[in] MemoryRangesSize - Number of entries in Memory Ranges @retval Status code --*/ EFI_STATUS SortRanges ( IN OUT MEMORY_RANGE *MemoryRanges, IN UINTN MemoryRangesSize ) { UINT32 Count, Index; MEMORY_RANGE TempRange; MEMORY_RANGE *Range; Range = MemoryRanges; if (MemoryRangesSize == 0) { while (Range->Start != END_RANGES) { MemoryRangesSize++; Range++; } } for (Count = 1; Count < MemoryRangesSize; Count++) { for (Index = 0; Index < MemoryRangesSize - Count; Index++) { if (MemoryRanges[Index].Start > MemoryRanges[Index + 1].Start) { TempRange.Start = MemoryRanges[Index].Start; TempRange.End = MemoryRanges[Index].End; MemoryRanges[Index].Start = MemoryRanges[Index + 1].Start; MemoryRanges[Index].End = MemoryRanges[Index + 1].End; MemoryRanges[Index + 1].Start = TempRange.Start; MemoryRanges[Index + 1].End = TempRange.End; } } } return EFI_SUCCESS; } /** Subtracts set of memory ranges from other set of memory ranges. Resulting set will contain ranges covering InputRanges with SubRanges excluded. Both InputRanges and SubRanges must be provided in ascending order by Start address. Output set is also sorted. All sets end with a range which Start address is END_RANGES. @param[out] OutputRanges - Output buffer @param[in] OutputRangesSize - Number of entries in output buffer @param[in] InputRanges - Input ranges to substract from @param[in] SubRanges - Ranges to be excluded from InputRanges @retval EFI_SUCCESS - Success @retval EFI_BUFFER_TOO_SMALL - Not enough space in output buffer **/ STATIC EFI_STATUS SubtractRanges ( OUT MEMORY_RANGE *OutputRanges, IN UINTN OutputRangesSize, IN MEMORY_RANGE *InputRanges, IN MEMORY_RANGE *SubRanges ) { MEMORY_RANGE *SubRange; MEMORY_RANGE Current; UINTN OutIndex; ASSERT (OutputRanges != InputRanges); ASSERT (OutputRanges != SubRanges); DEBUG ((EFI_D_INFO, "Sub ranges Before Sorting:\n")); PrintRanges (SubRanges); SortRanges (SubRanges, 0); DEBUG ((EFI_D_INFO, "Sub ranges After Sorting:\n")); PrintRanges (SubRanges); OutIndex = 0; while (InputRanges->Start != END_RANGES) { Current.Start = InputRanges->Start; Current.End = InputRanges->End; SubRange = SubRanges; while (SubRange->Start != END_RANGES) { if ((Current.Start < SubRange->End) && (Current.End > SubRange->Start)) { if (Current.Start < SubRange->Start) { if (OutIndex + 1 >= OutputRangesSize) { return EFI_BUFFER_TOO_SMALL; } OutputRanges[OutIndex].Start = Current.Start; OutputRanges[OutIndex].End = MIN (Current.End, SubRange->Start); ++OutIndex; } if (Current.End > SubRange->End) { Current.Start = SubRange->End; } else { Current.Start = END_RANGES; } } ++SubRange; } if (Current.Start != END_RANGES) { if (OutIndex + 1 >= OutputRangesSize) { return EFI_BUFFER_TOO_SMALL; } OutputRanges[OutIndex].Start = Current.Start; OutputRanges[OutIndex].End = Current.End; ++OutIndex; } ++InputRanges; } ASSERT (OutIndex < OutputRangesSize); OutputRanges[OutIndex].Start = END_RANGES; return EFI_SUCCESS; } /** Returns total length of ranges in the set Ranges. @param[in] Ranges - Array of memory ranges. @retval The size of memory range **/ STATIC UINT64 SumRanges ( IN MEMORY_RANGE *Ranges ) { UINT64 Size; Size = 0; while (Ranges->Start != END_RANGES) { Size += RANGE_LENGTH (Ranges); ++Ranges; } return Size; } /** This routing is executed to clear all pending wake events and then enable wake events. @param[in] EnablePme - set to TRUE to enable PME event @retval EFI_SUCCESS - Operation successfully performed **/ EFI_STATUS RapidStartClearAndEnablePmeEvent ( IN BOOLEAN EnablePme ) { RAPID_START_PPI *This; EFI_STATUS Status; RAPID_START_INSTANCE *Instance; RAPID_START_PERSISTENT_DATA *RapidStartData; RAPID_START_MEM_DATA *RapidStartMemData; PCH_SERIES PchSeries; UINT32 PmBase; PchSeries = GetPchSeries(); PmBase = MmioRead32 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_ACPI_BASE) ) & B_PCH_LPC_ACPI_BASE_BAR; Status = PeiServicesLocatePpi (&gRapidStartPpiGuid, 0, NULL, (VOID **) &This); ASSERT_EFI_ERROR (Status); Instance = RAPID_START_INSTANCE_FROM_THIS (This); RapidStartData = &Instance->Data; RapidStartMemData = RAPID_START_MEM_DATA_PTR (RapidStartData); DEBUG ((EFI_D_ERROR, "RapidStart: RapidStartMemData @ %X\n", RapidStartMemData)); /// /// Clear all pending wake events /// RapidStartClearOemPmeEvent (); if (PchSeries == PchLp) { IoWrite32 (PmBase + R_PCH_ACPI_GPE0_STS_127_96, ~0u); } else if (PchSeries == PchH) { IoWrite32 (PmBase + R_PCH_ACPI_GPE0a_STS, ~0u); IoWrite32 (PmBase + R_PCH_ACPI_GPE0b_STS, ~0u); } IoWrite16 ( PmBase + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_WAK | B_PCH_ACPI_PM1_STS_RTC | B_PCH_ACPI_PM1_STS_PWRBTN ); /// /// Enable wake events /// if (EnablePme == TRUE) { if (PchSeries == PchLp) { IoOr32 (PmBase + R_PCH_ACPI_GPE0_EN_127_96, RapidStartMemData->GPE0); } else if (PchSeries == PchH) { IoOr32 (PmBase + R_PCH_ACPI_GPE0a_EN, RapidStartMemData->GPE0a); IoOr32 (PmBase + R_PCH_ACPI_GPE0b_EN, RapidStartMemData->GPE0b); } } return EFI_SUCCESS; } /** RapidStart End of PEI handler. @param[in] PeiServices - Pointer to PEI Services Table. @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that caused this function to execute. @param[in] Ppi - Pointer to the PPI data associated with this function. @retval EFI_SUCCESS - Ppi callback function executed successfully **/ STATIC EFI_STATUS RapidStartS4AtEndOfPei ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ) { UINT32 PmBase; EFI_STATUS Status; RAPID_START_PPI *This; RAPID_START_INSTANCE *Instance; RAPID_START_PERSISTENT_DATA *RapidStartData; DEBUG ((EFI_D_INFO, "RapidStartS4AtEndOfPei ()\n")); PmBase = MmioRead32 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_ACPI_BASE) ) & B_PCH_LPC_ACPI_BASE_BAR; Status = PeiServicesLocatePpi (&gRapidStartPpiGuid, 0, NULL, (VOID **) &This); ASSERT_EFI_ERROR (Status); Instance = RAPID_START_INSTANCE_FROM_THIS (This); RapidStartData = &Instance->Data; RapidStartWANetDetect (PeiServices, RapidStartData->WlanMmioSpace); RapidStartClearAndEnablePmeEvent (TRUE); DEBUG ((EFI_D_ERROR, "RapidStart: Switching to S4\n")); IoAndThenOr32 ( PmBase + R_PCH_ACPI_PM1_CNT, (UINT32)~B_PCH_ACPI_PM1_CNT_SLP_TYP, (UINT32) (V_PCH_ACPI_PM1_CNT_S4 | B_PCH_ACPI_PM1_CNT_SLP_EN) ); EFI_DEADLOOP (); return EFI_SUCCESS; } /** RapidStart End of PEI handler. @param[in] PeiServices - Pointer to PEI Services Table. @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that caused this function to execute. @param[in] Ppi - Pointer to the PPI data associated with this function. @retval EFI_SUCCESS - Ppi callback function executed successfully **/ STATIC EFI_STATUS RapidStartRecoveryAtEndOfPei ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ) { UINT32 PmBase; DEBUG ((EFI_D_INFO, "RapidStartRecoveryAtEndOfPei ()\n")); RapidStartClearAndEnablePmeEvent (TRUE); PmBase = MmioRead32 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_ACPI_BASE) ) & B_PCH_LPC_ACPI_BASE_BAR; DEBUG ((EFI_D_ERROR, "RapidStart: Switching state back to S3\n")); IoAndThenOr32 ( PmBase + R_PCH_ACPI_PM1_CNT, (UINT32)~B_PCH_ACPI_PM1_CNT_SLP_TYP, (UINT32) (V_PCH_ACPI_PM1_CNT_S3 | B_PCH_ACPI_PM1_CNT_SLP_EN) ); EFI_DEADLOOP (); return EFI_SUCCESS; } /** Executes necessary RapidStart late initialization after Boot Script done. @param[in] PeiServices - Pointer to PEI Services Table. @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that caused this function to execute. @param[in] Ppi - Pointer to the PPI data associated with this function. @retval EFI_SUCCESS - RapidStart initialization successful **/ STATIC EFI_STATUS RapidStartExitAtEndOfPei ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ) { DEBUG ((EFI_D_INFO, "RapidStartExitAtEndOfPei ()\n")); return RapidStartEnableAcpi (PeiServices); } /** PEI_PERMANENT_MEMORY_INSTALLED_PPI handler. RapidStart transition is triggered here in case RapidStart is postoponed untill memory available in PEI. @param[in] PeiServices - Pointer to PEI Services Table. @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that caused this function to execute. @param[in] Ppi - Pointer to the PPI data associated with this function. @retval EFI_SUCCESS **/ STATIC EFI_STATUS RapidStartMemoryInstalled ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ) { RAPID_START_PPI *RapidStartPpi; EFI_STATUS Status; DEBUG ((EFI_D_INFO, "RapidStartMemoryInstalled ()\n")); Status = (*PeiServices)->LocatePpi (PeiServices, &gRapidStartPpiGuid, 0, NULL, (VOID **) &RapidStartPpi); ASSERT_EFI_ERROR (Status); RapidStartDoTransition (RapidStartPpi); return EFI_SUCCESS; } #ifdef EFI_DEBUG /** Dump MCH relevant registers **/ STATIC VOID DumpMcRegisters ( VOID ) { UINTN MchBase; MchBase = MmPciAddress (0, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN, 0); DEBUG ((EFI_D_INFO, "TOM : %lx\n", MmioRead64 (MchBase + R_SA_TOM))); DEBUG ((EFI_D_INFO, "TSEGMB : %x\n", MmioRead32 (MchBase + R_SA_TSEGMB))); DEBUG ((EFI_D_INFO, "TOLUD : %x\n", MmioRead32 (MchBase + R_SA_TOLUD))); DEBUG ((EFI_D_INFO, "TOUUD : %lx\n", MmioRead64 (MchBase + R_SA_TOUUD))); DEBUG ((EFI_D_INFO, "BDSM : %x\n", MmioRead32 (MchBase + R_SA_BDSM))); DEBUG ((EFI_D_INFO, "BGSM : %x\n", MmioRead32 (MchBase + R_SA_BGSM))); DEBUG ((EFI_D_INFO, "DPR : %x\n", MmioRead32 (MchBase + R_SA_DPR))); DEBUG ((EFI_D_INFO, "MESEG_B : %lx\n", MmioRead64 (MchBase + R_SA_MESEG_BASE))); DEBUG ((EFI_D_INFO, "MESEG_M : %lx\n", MmioRead64 (MchBase + R_SA_MESEG_MASK))); DEBUG ((EFI_D_INFO, "REMAPBASE: %lx\n", MmioRead64 (MchBase + R_SA_REMAPBASE))); DEBUG ((EFI_D_INFO, "REMAPLIM : %lx\n", MmioRead64 (MchBase + R_SA_REMAPLIMIT))); DEBUG ((EFI_D_INFO, "SMRAMC : %02x\n", MmioRead8 (MchBase + R_SA_SMRAMC))); DEBUG ((EFI_D_INFO, "GGC : %04x\n", MmioRead16 (MchBase + R_SA_GGC))); } /** Show Hexadecimal number by debug macro @param[in] Ptr - Pointer to buffer of number that will be showed @param[in] Length - The length of the number buffer **/ VOID PrintHex ( IN UINT8 *Ptr, IN UINTN Length ) { while (Length--) { DEBUG ((EFI_D_INFO, "%02x", *Ptr++)); } DEBUG ((EFI_D_INFO, "\n")); } #endif /** Retrieves SMRAM areas ffrom SMRAM HOB, and places the ranges found in SmRamRanges array. @param[out] SmRamRanges - Output buffer @param[in] SmRamRangesSize - Number of entries in output buffer @retval EFI_SUCCESS @retval EFI_NOT_FOUND @retval EFI_BUFFER_TOO_SMALL **/ STATIC EFI_STATUS GetSmRamRanges ( OUT MEMORY_RANGE *SmRamRanges, IN UINTN SmRamRangesSize, IN UINT32 IedSize ) { EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *SmmDescBlock; UINTN Index; VOID *Hob; Hob = GetFirstGuidHob (&gEfiSmmPeiSmramMemoryReserve); if (Hob == NULL) { DEBUG ((EFI_D_ERROR, "SMRAM HOB not found!\n")); ASSERT (0); return EFI_NOT_FOUND; } SmmDescBlock = (VOID *) ((UINT8 *) Hob + sizeof (EFI_HOB_GUID_TYPE)); DEBUG ((EFI_D_INFO, "Found %d SMM ranges\n", SmmDescBlock->NumberOfSmmReservedRegions)); ASSERT (SmRamRangesSize > SmmDescBlock->NumberOfSmmReservedRegions); if (SmRamRangesSize <= SmmDescBlock->NumberOfSmmReservedRegions) { return EFI_BUFFER_TOO_SMALL; } for (Index = 0; Index < SmmDescBlock->NumberOfSmmReservedRegions; ++Index) { SmRamRanges[Index].Start = SmmDescBlock->Descriptor[Index].PhysicalStart; if (SmRamRanges[Index].Start > 0x100000) { // // This is TSEG SMRAM range. // IED region is also part of SMRAM but not reported in SMRAM Hob so here include IED for Rapid Start saving/restoring. // DEBUG ((EFI_D_INFO, "Ied size %X\n", IedSize)); SmRamRanges[Index].End = SmRamRanges[Index].Start + SmmDescBlock->Descriptor[Index].PhysicalSize + IedSize; } } SmRamRanges[Index].Start = END_RANGES; return EFI_SUCCESS; } /** Retrieves DPR areas from SA register, and places the ranges found in DprRanges array. @param[out] DprRanges - Output buffer @retval EFI_SUCCESS @retval EFI_NOT_FOUND **/ STATIC EFI_STATUS GetDprRanges ( OUT MEMORY_RANGE *DprRanges ) { EFI_STATUS Status; UINTN Index; UINTN MchBase; UINT32 DprData; UINT32 DprSize; Status = EFI_NOT_FOUND; Index = 0; MchBase = MmPciAddress (0, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN, 0); DprData = MmioRead32 (MchBase + R_SA_DPR); DprSize = (DprData & V_DPR_DPRSIZE_MASK) << (N_SA_DPR_TOPOFDPR_OFFSET - N_DPR_DPRSIZE_OFFSET); /// /// Check whether DPR protection and DPR is enabled or not. /// if ((DprData & (B_SA_DPR_PRS_MASK + B_SA_DPR_EPM_MASK)) == (B_SA_DPR_PRS_MASK + B_SA_DPR_EPM_MASK)) { DprRanges[Index].Start = (DprData & B_SA_DPR_TOPOFDPR_MASK) - DprSize; DprRanges[Index].End = DprData & B_SA_DPR_TOPOFDPR_MASK; DEBUG ((EFI_D_INFO, "DPR Start = %08lx, End = %08lx\n", DprRanges[Index].Start, DprRanges[Index].End)); Index ++; Status = EFI_SUCCESS; } DprRanges[Index].Start = END_RANGES; return Status; } /** Constructs memory ranges to be saved/restored by RapidStart. Reserved areas are excluded. @param[out] OutMemoryRanges - Output buffer @param[in] OutMemoryRangesSize - Number of entries in output buffer @param[in] RapidStartData - A data buffer stored RapidStart internal non-volatile information. @retval EFI_SUCCESS **/ STATIC EFI_STATUS BuildMemoryRanges ( OUT MEMORY_RANGE *OutMemoryRanges, IN UINTN OutMemoryRangesSize, IN RAPID_START_PERSISTENT_DATA *RapidStartData ) { MEMORY_RANGE MainRanges[4]; MEMORY_RANGE ResvRanges[3]; EFI_STATUS Status; UINTN Index; /// /// Always exclude Legacy SMRAM ranges (A0000~BFFFF) since this range can not perform DMA. /// If Legacy SMRAM is used it will be handled separately. /// Index = 0; MainRanges[Index].Start = 0; MainRanges[Index].End = LEGACY_SMRAM_BASE; ++Index; MainRanges[Index].Start = LEGACY_SMRAM_BASE + LEGACY_SMRAM_SIZE; MainRanges[Index].End = RapidStartData->Tolm; ++Index; if (RapidStartData->Tohm > MEM_EQU_4GB) { MainRanges[Index].Start = MEM_EQU_4GB; MainRanges[Index].End = RapidStartData->Tohm; ++Index; } ASSERT (Index < VARIABLE_SIZE (MainRanges)); MainRanges[Index].Start = END_RANGES; Index = 0; ResvRanges[Index].Start = RapidStartData->RapidStartMem; ResvRanges[Index].End = RapidStartData->RapidStartMem + RapidStartData->RapidStartMemSize; ++Index; #ifdef RAPID_START_ON_MEMORY_INSTALLED ResvRanges[Index].Start = RapidStartData->AcpiReservedMemoryBase; ResvRanges[Index].End = RapidStartData->AcpiReservedMemoryBase + RapidStartData->AcpiReservedMemorySize; ++Index; #endif ASSERT (Index < VARIABLE_SIZE (ResvRanges)); ResvRanges[Index].Start = END_RANGES; /// /// Exclude reserved ranges /// Status = SubtractRanges ( OutMemoryRanges, OutMemoryRangesSize, MainRanges, ResvRanges ); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } #ifdef RAPID_START_NO_SMRAM_INTEGRITY_CHECK #define HASHING_CONTEXT typedef enum { EMPTY } #define HashingInit(c, r) EFI_SUCCESS #define HashingProcessChunk(c) TRUE #define HashingCompleteAndVerify(c, t) EFI_SUCCESS #else typedef struct { MEMORY_RANGE *Range; UINT8 *Ptr; VOID *HashState; } HASHING_CONTEXT; #define HASHING_CHUNK (4 * EFI_PAGE_SIZE) /** Initializes secure hash algorithm. @param[in] Context - Hashing context @param[in] Ranges - Memory ranges to be included in resulting hash @retval EFI_SUCCESS Hashing process completed @retval ERROR Return the error code if PeiServicesAllocatePool () failed **/ STATIC EFI_STATUS HashingInit ( HASHING_CONTEXT *Context, MEMORY_RANGE *Ranges ) { UINTN HashStatetSize; EFI_STATUS Status; Context->Range = Ranges; Context->Ptr = (VOID *) (UINTN) Ranges->Start; HashStatetSize = IfsSecureHashGetContextSize (); ASSERT (HashStatetSize != 0); Status = PeiServicesAllocatePool (HashStatetSize, &Context->HashState); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return Status; } RapidStartSecureHashInit (Context->HashState); return EFI_SUCCESS; } /** Processes next chunk of memory. @param[in] Context - Hashing context @retval TRUE if hashing complete, FALSE othervise. **/ STATIC BOOLEAN HashingProcessChunk ( HASHING_CONTEXT *Context ) { UINTN Size; if (Context->Range->Start == END_RANGES) { return TRUE; } DEBUG ((EFI_D_INFO, "#")); Size = (UINTN) Context->Range->End - (UINTN) Context->Ptr; if (Size > HASHING_CHUNK) { Size = HASHING_CHUNK; } RapidStartSecureHashUpdate (Context->HashState, (VOID *) Context->Ptr, Size); Context->Ptr += Size; if ((UINTN) Context->Ptr == (UINTN) Context->Range->End) { ++Context->Range; Context->Ptr = (VOID *) (UINTN) Context->Range->Start; } return FALSE; } /** Finishes hashing and saves/verifies resulting value depending on RapidStart transition. @param[in] Context - Hashing context @param[in] Instance - RapidStart instance @retval EFI_SECURITY_VIOLATION - SMRAM content has changed. @retval EFI_SUCCESS - SMRAM content no change. **/ STATIC EFI_STATUS HashingCompleteAndVerify ( HASHING_CONTEXT *Context, RAPID_START_INSTANCE *Instance ) { UINT8 Hash[RAPID_START_SECURE_HASH_LENGTH]; EFI_STATUS Status; DEBUG ((EFI_D_INFO, "HashingCompleteAndVerify...\n")); Status = EFI_SUCCESS; while (!HashingProcessChunk (Context)) { } RapidStartSecureHashFinal (Context->HashState, Hash); #ifdef EFI_DEBUG PrintHex (Hash, RAPID_START_SECURE_HASH_LENGTH); #endif if (Instance->Transition == RapidStartExit) { if (CompareMem (Hash, Instance->SmRamHash, RAPID_START_SECURE_HASH_LENGTH) != 0) { DEBUG ((EFI_D_ERROR, "Error: SMRAM content has changed!\n")); Status = EFI_SECURITY_VIOLATION; } ZeroMem (Instance->SmRamHash, RAPID_START_SECURE_HASH_LENGTH); } else { Status = RapidStartSaveSecureHash (Hash); ASSERT_EFI_ERROR (Status); } ZeroMem (Hash, RAPID_START_SECURE_HASH_LENGTH); ZeroMem (Context->HashState, IfsSecureHashGetContextSize ()); return Status; } #endif /** Check ME status and wait for ME init done @retval EFI_SUCCESS - ME Init done @retval EFI_TIMEOUT - ME is not ready after timeout occurred @retval EFI_DEVICE_ERROR - Error reported by ME F/W **/ STATIC EFI_STATUS WaitMeInitDone ( VOID ) { EFI_PEI_SERVICES **PeiServices; PEI_HECI_PPI *HeciPpi; EFI_STATUS Status; UINT32 MeStatus; UINT32 MeMode; UINTN Timeout; DEBUG ((EFI_D_INFO, "WaitMeInitDone()\n")); PeiServices = GetPeiServicesTablePointer (); Status = (*PeiServices)->LocatePpi ( PeiServices, &gPeiHeciPpiGuid, 0, NULL, (VOID **) &HeciPpi ); ASSERT_EFI_ERROR (Status); Status = HeciPpi->GetMeMode (PeiServices, &MeMode); ASSERT_EFI_ERROR (Status); /// /// If ME is in ME_DEBUG mode, return success. /// if (MeMode == ME_MODE_DEBUG) { return EFI_SUCCESS; } if (MeMode != ME_MODE_NORMAL) { return EFI_DEVICE_ERROR; } Timeout = 0; while (Timeout < ME_FID_TIMEOUT) { Status = HeciPpi->GetMeStatus (PeiServices, &MeStatus); ASSERT_EFI_ERROR (Status); if (ME_STATUS_IS_ME_FW_INIT_COMPLETE (MeStatus)) { return EFI_SUCCESS;; } PchPmTimerStall (1000); Timeout++; } DEBUG ((EFI_D_ERROR, "ME init timeout!\n")); return EFI_TIMEOUT; } /** Calls Func for each port in PortMap. @param[in] Ahci - SATA controller information structure @param[in] Func - Pointer to function to be called with Ahci and port number passed as arguments @param[in] PortMap - Bitmap of ports for wich Func is to be called **/ STATIC VOID ForEachAhciPort ( IN AHCI_CONTEXT *Ahci, IN EFI_STATUS (*Func) (AHCI_CONTEXT*, UINTN), IN UINT32 PortMap ) { UINTN Port; for (Port = 0; Port <= 31; ++Port) { if (PortMap & (1u << Port)) { Func (Ahci, Port); } } return; } /** Issues STANDBY_IMMEDIATE command to given SATA port. Port is initialized first and disabled afterwards. @param Ahci - SATA controller information structure @param Port - Port number to issue command to @retval EFI_SUCCESS - fucntion executed successfully @retval EFI_TIMEOUT - Timeout occured @retval EFI_DEVICE_ERROR - AHCI port initialization failed or Command failed **/ STATIC EFI_STATUS StandbyAhciPort ( IN AHCI_CONTEXT *Ahci, IN UINTN Port ) { AHCI_CONTEXT AhciTmp; EFI_STATUS Status; DEBUG ((EFI_D_INFO, "StandbyAhciPort(%d)\n", Port)); AhciTmp = *Ahci; AhciTmp.Port = Port; Status = AhciPortInit (&AhciTmp); if (Status == EFI_SUCCESS) { Status = AhciSimpleCommand (&AhciTmp, ATA_CMD_STANDBY_IMMEDIATE); } AhciPortDone (&AhciTmp); return Status; } /** Initialize Ahci port for RapidStart accessing SSD in early stage. @param[in] Instance - Instance contains required data and function for RapidStart **/ STATIC VOID RapidStartInitAhci ( IN RAPID_START_INSTANCE *Instance ) { AHCI_CONTEXT *Ahci; RAPID_START_PERSISTENT_DATA *RapidStartData; DEBUG ((EFI_D_INFO, "InitAhci\n")); Ahci = &Instance->Ahci; RapidStartData = &Instance->Data; Ahci->Abar = (UINT32) RapidStartData->MmioSpace; Ahci->PortBase = (UINT32) RapidStartData->RapidStartMem; Ahci->PortSize = RapidStartData->RapidStartMemSize - sizeof (RAPID_START_MEM_DATA) - RapidStartData->ZeroBitmapSize - RapidStartData->Crc32RecordSize; Ahci->Port = RapidStartData->StoreSataPort; DEBUG ((EFI_D_INFO, "Ahci.Abar = %08x\n", Ahci->Abar)); DEBUG ((EFI_D_INFO, "Ahci.Port = %d\n", Ahci->Port)); DEBUG ((EFI_D_INFO, "Ahci.PortBase = %08x\n", Ahci->PortBase)); DEBUG ((EFI_D_INFO, "Ahci.PortSize = %08x\n", Ahci->PortSize)); /// /// Restore SATA ports configuration /// MmioWrite8 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_SATA, PCI_FUNCTION_NUMBER_PCH_SATA, R_PCH_SATA_PCS), RapidStartData->SataPortConfiguration ); AhciInit (Ahci); AhciSpinUp (Ahci); } #define RETURN_ON_ERROR(expr) \ do { \ EFI_STATUS _status; \ _status = (expr); \ if (EFI_ERROR (_status)) { \ return _status; \ } \ } while (0) /** Transfer SMRAM ranges to/from RapidStart partition. Legacy SMRAM 0xA0000-0xC0000 is handled indirectly. @param[in] Ahci - AHCI context @param[in,out] AtaCmd - ATA command structure @param[in] Transition - RapidStart transition @param[in] SmRamRanges - Array of SMRAM ranges @retval EFI_STATUS - the error code returned by AhciPostCommand () if failed. @retval EFI_SUCCESS - Hasing SMRAM process completed. **/ STATIC EFI_STATUS RapidStartHandleSmRam ( IN AHCI_CONTEXT *Ahci, IN OUT ATA_COMMAND *AtaCmd, IN RAPID_START_TRANSITION Transition, IN MEMORY_RANGE *SmRamRanges ) { MEMORY_RANGE *Range; UINTN CmdMask; DEBUG ((EFI_D_INFO, "SMRAM handling...\n")); PrintRanges (SmRamRanges); if (Transition == RapidStartEntry) { /// /// Ensure original LEGACY_SMRAM_BUFFER data has been stored into RapidStart Store before we modifying it. /// RETURN_ON_ERROR (AhciWaitAllComplete (Ahci)); CopyMem ((VOID *) LEGACY_SMRAM_BUFFER, (VOID *) LEGACY_SMRAM_BASE, (UINTN) LEGACY_SMRAM_SIZE); } CmdMask = 0; Range = SmRamRanges; while (Range->Start != END_RANGES) { AtaCmd->SectorCount = MEM_TO_SECT (RANGE_LENGTH (Range)); if (Range->Start == LEGACY_SMRAM_BASE) { ASSERT (Range->End == LEGACY_SMRAM_BASE + LEGACY_SMRAM_SIZE); AtaCmd->MemAddr = LEGACY_SMRAM_BUFFER; RETURN_ON_ERROR (AhciPostCommand (Ahci, AtaCmd, &CmdMask)); } else { AtaCmd->MemAddr = Range->Start; CmdMask = ~CmdMask; RETURN_ON_ERROR (AhciPostCommand (Ahci, AtaCmd, &CmdMask)); CmdMask = ~CmdMask; } ++Range; } AhciWaitComplete (Ahci, CmdMask); if (Transition == RapidStartExit) { CopyMem ((VOID *) LEGACY_SMRAM_BASE, (VOID *) LEGACY_SMRAM_BUFFER, (UINTN) LEGACY_SMRAM_SIZE); } return EFI_SUCCESS; } /** Performs AHCI commands required to save or restore memory content. @param[in] Instance - RapidStart instance @param[in] MainRanges - Ranges of memory to be transferred @param[in] SmRamRanges - SMRAM ranges, to by handled by RapidStartHandleSmRam @retval EFI_SUCCESS - RapidStart transition succeeded @retval EFI_TIMEOUT - AHCI timeout occured @retval EFI_DEVICE_ERROR - AHCI device not ready @retval EFI_NOT_STARTED - Transition cancellation happened, abort the transition. @retval EFI_NO_MEDIA - No RapidStart Store available or RapidStart store has been changed. Abort RapidStart transition. @retval EFI_BAD_BUFFER_SIZE - Memory configuration changed. Abort RapidStart transition. **/ STATIC EFI_STATUS RapidStartMemoryDiskTransfer ( IN RAPID_START_INSTANCE *Instance, IN MEMORY_RANGE *MainRanges, IN MEMORY_RANGE *SmRamRanges ) { RAPID_START_PERSISTENT_DATA *RapidStartData; AHCI_CONTEXT *Ahci; RAPID_START_TRANSITION Transition; ATA_COMMAND AtaCmd; MEMORY_RANGE *MemRanges; MEMORY_RANGE *Range; EFI_STATUS Status; UINT32 *ZeroPageBitMap; UINT32 SectorsLeft; HASHING_CONTEXT Hashing; BOOLEAN HashingDone; UINT64 DataCount; UINT32 *SmramBuffer; UINT32 ZeroBitmapForSmramBuffer; UINT64 LbaForData; UINT64 SmRamBufferLba; EFI_STATUS GfxDisplayStatus; PEI_GFX_PPI *PeiGfxPpi; GFX_RESTORE_MEM_TABLE *GfxRestoreMemTable; GFX_RESTORE_MEM_TABLE *FbRestoreMemTable; UINT32 Index, GfxIndex, MemIndex; MEMORY_RANGE *GfxResvRanges; MEMORY_RANGE *TempMemRanges; UINT64 PageAlignedDssAddr; UINT64 PageAlignedDssSize; MemRanges = (MEMORY_RANGE *) AllocatePool (sizeof(MEMORY_RANGE) * (MAX_MEMORY_RANGES + 1)); if (MemRanges == NULL) { DEBUG ((EFI_D_ERROR, "Failed to allocate memory for MemRanges! \n")); return EFI_OUT_OF_RESOURCES; } GfxResvRanges = (MEMORY_RANGE *) AllocatePool (sizeof(MEMORY_RANGE) * (MAX_GFX_MEMORY_RANGES + 1)); if (GfxResvRanges == NULL) { DEBUG ((EFI_D_ERROR, "Failed to allocate memory for GfxResvRanges! \n")); return EFI_OUT_OF_RESOURCES; } TempMemRanges = (MEMORY_RANGE *) AllocatePool (sizeof(MEMORY_RANGE) * (MAX_MEMORY_RANGES + 1)); if (GfxResvRanges == NULL) { DEBUG ((EFI_D_ERROR, "Failed to allocate memory for TempMemRanges! \n")); return EFI_OUT_OF_RESOURCES; } RapidStartData = &Instance->Data; Ahci = &Instance->Ahci; Transition = Instance->Transition; /// /// Exclude SMRAM ranges as they are handled separetely /// Status = SubtractRanges ( MemRanges, VARIABLE_SIZE (MemRanges), MainRanges, SmRamRanges ); ASSERT_EFI_ERROR (Status); AtaCmd.Auxiliary.Data = 0; if (Transition == RapidStartEntry) { if (RapidStartData->HybridHardDisk == 1) { Status = AhciHybridHardDiskSupport (Ahci); if (Status == EFI_SUCCESS) { if (Ahci->TotalRemainingCacheCapacityInSector < RapidStartData->StoreSectors) { return EFI_BAD_BUFFER_SIZE; } AtaCmd.Auxiliary.r.HybridPriority = Ahci->HybridInfo.HybridPriority; AtaCmd.Auxiliary.r.HybridInfoValid = 1; } else if (Status != EFI_UNSUPPORTED) { return Status; } } Ahci->PollCancellation = TRUE; AtaCmd.Command = ATA_CMD_WRITE_DMA_EXT; AtaCmd.Direction = AHCI_DIR_HOST2DEV; } else { Ahci->PollCancellation = FALSE; AtaCmd.Command = ATA_CMD_READ_DMA_EXT; AtaCmd.Direction = AHCI_DIR_DEV2HOST; if (RapidStartData->HybridHardDisk == 1) { if (((Ahci->Identify[ATA_ID_DEV_HYBRID_FEATURE_SUPPORT] & B_ATA_ID_DEV_HYBRID_FEATURE_SUPPORT) & (Ahci->Identify[ATA_ID_DEV_HYBRID_FEATURE_ENABLE] & B_ATA_ID_DEV_HYBRID_FEATURE_ENABLE)) ) { AtaCmd.Auxiliary.r.HybridPriority = 0; AtaCmd.Auxiliary.r.HybridInfoValid = 1; } } } AtaCmd.Feature = 0; #ifdef RAPID_START_WHOLE_MEMORY_CHECK /// /// Save CRC32 for memory below 4GB /// if (Transition == RapidStartEntry) { SaveOrCompareCrc32 (FALSE, 0, RapidStartData->Tolm, RapidStartData); } #endif /// /// Head sector store the Zero Page Bitmap, so save/restore it first /// ZeroPageBitMap = RAPID_START_ZERO_PAGE_BITMAP_PTR (RapidStartData); /// /// For Entry, store RapidStart Store UID in the head of Zero Page Bitmap. /// if (Transition == RapidStartEntry) { *(UINT64 *) (ZeroPageBitMap) = RapidStartData->RapidStartStoreUid; } AtaCmd.MemAddr = (UINTN) ZeroPageBitMap; AtaCmd.Lba = RapidStartData->StoreLbaAddr; AtaCmd.SectorCount = MEM_TO_SECT (RapidStartData->ZeroBitmapSize + RapidStartData->Crc32RecordSize); RETURN_ON_ERROR (AhciPostCommand (Ahci, &AtaCmd, NULL)); LbaForData = RapidStartData->StoreLbaAddr + MEM_TO_SECT (RapidStartData->ZeroBitmapSize + RapidStartData->Crc32RecordSize); GfxDisplayStatus = EFI_UNSUPPORTED; if (RapidStartData->DssAddress != (UINT64)NULL) { PageAlignedDssAddr = RapidStartData->DssAddress & ~EFI_PAGE_MASK; PageAlignedDssSize = (EFI_SIZE_TO_PAGES(PageAlignedDssAddr) < EFI_SIZE_TO_PAGES(RapidStartData->DssAddress) ) ? EFI_PAGE_SIZE : 0; PageAlignedDssSize += RapidStartData->DssSize; AtaCmd.MemAddr = (UINTN) PageAlignedDssAddr; AtaCmd.Lba = LbaForData; AtaCmd.SectorCount = MEM_TO_SECT (PageAlignedDssSize); RETURN_ON_ERROR (AhciPostCommand (Ahci, &AtaCmd, NULL)); LbaForData += MEM_TO_SECT (PageAlignedDssSize); GfxIndex = 0; GfxResvRanges[GfxIndex].Start = PageAlignedDssAddr; GfxResvRanges[GfxIndex].End = PageAlignedDssAddr + PageAlignedDssSize; ++GfxIndex; // // save/restore GTT, GMM... ets Gfx memory related Range // GfxDisplayStatus = PeiServicesLocatePpi (&gPeiGfxPpiGuid, 0, NULL, &PeiGfxPpi); ASSERT_EFI_ERROR (GfxDisplayStatus); if (!EFI_ERROR (GfxDisplayStatus)) { if (Transition == RapidStartExit) { RETURN_ON_ERROR (AhciWaitAllComplete (Ahci)); } GfxDisplayStatus = PeiGfxPpi->GetRestoreMemTable (GetPeiServicesTablePointer (), &GfxRestoreMemTable); DEBUG ((EFI_D_ERROR, "Gfx GetRestoreMemTable Status = %r, NumberofEntry = %x\n", GfxDisplayStatus, GfxRestoreMemTable->NumOfEntry)); if (!EFI_ERROR (GfxDisplayStatus) && (GfxRestoreMemTable->NumOfEntry <= GFX_RESTORE_RANGE)) { for (Index = 0; Index < GfxRestoreMemTable->NumOfEntry; Index++) { DEBUG ((EFI_D_ERROR, "Gfx related memory Ranges [%x] - BaseAddress is %lx, Size is %lx\n", Index, GfxRestoreMemTable->MemRangeEntry[Index].BaseAddress, GfxRestoreMemTable->MemRangeEntry[Index].Size)); GfxResvRanges[GfxIndex].Start = GfxRestoreMemTable->MemRangeEntry[Index].BaseAddress; GfxResvRanges[GfxIndex].End = GfxRestoreMemTable->MemRangeEntry[Index].BaseAddress + GfxRestoreMemTable->MemRangeEntry[Index].Size; MemIndex = 0; while (MemRanges[MemIndex].Start != END_RANGES) { if (GfxResvRanges[GfxIndex].Start >= MemRanges[MemIndex].Start && GfxResvRanges[GfxIndex].End <= MemRanges[MemIndex].End) { DEBUG ((EFI_D_ERROR, "Gfx related memory Ranges [%x] - BaseAddress is %lx, Size is %lx has been save(restore).\n", Index, GfxRestoreMemTable->MemRangeEntry[Index].BaseAddress, GfxRestoreMemTable->MemRangeEntry[Index].Size)); AtaCmd.MemAddr = (UINTN) GfxRestoreMemTable->MemRangeEntry[Index].BaseAddress; AtaCmd.Lba = LbaForData; AtaCmd.SectorCount = MEM_TO_SECT (GfxRestoreMemTable->MemRangeEntry[Index].Size); RETURN_ON_ERROR (AhciPostCommand (Ahci, &AtaCmd, NULL)); LbaForData += MEM_TO_SECT (GfxRestoreMemTable->MemRangeEntry[Index].Size); ++GfxIndex; break; } ++MemIndex; } } } // // save/restore Gfx Frame Buffer Ranges // if (RapidStartData->DisplayType == 1 && !EFI_ERROR (GfxDisplayStatus)) { if (Transition == RapidStartExit) { RETURN_ON_ERROR (AhciWaitAllComplete (Ahci)); } GfxDisplayStatus = PeiGfxPpi->GetRestoreFbRange (GetPeiServicesTablePointer (), &FbRestoreMemTable); DEBUG ((EFI_D_ERROR, "Gfx GetRestoreFbRange Status = %r, NumberofEntry = %x\n", GfxDisplayStatus, FbRestoreMemTable->NumOfEntry)); if (!EFI_ERROR (GfxDisplayStatus) && (FbRestoreMemTable->NumOfEntry <= FB_RESTORE_RANGE)) { for (Index = 0; Index < FbRestoreMemTable->NumOfEntry; Index++) { DEBUG ((EFI_D_ERROR, "Gfx FB memory Ranges [%x] - BaseAddress is %lx, Size is %lx\n", Index, FbRestoreMemTable->MemRangeEntry[Index].BaseAddress, FbRestoreMemTable->MemRangeEntry[Index].Size)); GfxResvRanges[GfxIndex].Start = FbRestoreMemTable->MemRangeEntry[Index].BaseAddress; GfxResvRanges[GfxIndex].End = FbRestoreMemTable->MemRangeEntry[Index].BaseAddress + FbRestoreMemTable->MemRangeEntry[Index].Size; MemIndex = 0; while (MemRanges[MemIndex].Start != END_RANGES) { if (GfxResvRanges[GfxIndex].Start >= MemRanges[MemIndex].Start && GfxResvRanges[GfxIndex].End <= MemRanges[MemIndex].End) { DEBUG ((EFI_D_ERROR, "Gfx FB memory Ranges [%x] - BaseAddress is %lx, Size is %lx has been save(restore).\n", Index, FbRestoreMemTable->MemRangeEntry[Index].BaseAddress, FbRestoreMemTable->MemRangeEntry[Index].Size)); AtaCmd.MemAddr = (UINTN) FbRestoreMemTable->MemRangeEntry[Index].BaseAddress; AtaCmd.Lba = LbaForData; AtaCmd.SectorCount = MEM_TO_SECT (FbRestoreMemTable->MemRangeEntry[Index].Size); RETURN_ON_ERROR (AhciPostCommand (Ahci, &AtaCmd, NULL)); LbaForData += MEM_TO_SECT (FbRestoreMemTable->MemRangeEntry[Index].Size); ++GfxIndex; break; } ++MemIndex; } } } } ASSERT (GfxIndex < VARIABLE_SIZE (GfxResvRanges)); GfxResvRanges[GfxIndex].Start = END_RANGES; // // Copy MemRanges to TempMemRanges // GfxDisplayStatus = SubtractRanges ( TempMemRanges, VARIABLE_SIZE (TempMemRanges), MainRanges, SmRamRanges ); ASSERT_EFI_ERROR (GfxDisplayStatus); // // Exclude Gfx related memory ranges as they are handled separetely // GfxDisplayStatus = SubtractRanges ( MemRanges, VARIABLE_SIZE (MemRanges), TempMemRanges, GfxResvRanges ); ASSERT_EFI_ERROR (GfxDisplayStatus); } } if (RapidStartData->DssAddress != (UINT64)NULL) { if (!EFI_ERROR (GfxDisplayStatus)) { switch(Transition) { case RapidStartEntry: DEBUG ((EFI_D_INFO, "Calling GfxDisplay in RapidStart entry flow...\n")); // GfxDisplayStatus = RapidStartGfxDisplayScreen(); break; case RapidStartExit: DEBUG ((EFI_D_INFO, "Calling GfxDisplay in RapidStart exit flow...\n")); if (RapidStartUnattendedWake() == FALSE) { GfxDisplayStatus = RapidStartGfxDisplayScreen(); } break; default: break; } } } if (Transition == RapidStartExit) { AtaCmd.Lba = LbaForData; if ((RapidStartHandleSmRam (Ahci, &AtaCmd, Transition, SmRamRanges) == EFI_DEVICE_ERROR) || (AhciWaitAllComplete (Ahci) == EFI_DEVICE_ERROR) ) { DEBUG ( ( EFI_D_ERROR, "\nEFI_DEVICE_ERROR in first DMA command, probably RapidStart Store changed. Aborted RapidStart resume.\n" ) ); RapidStartSetFlag (RAPID_START_FLAG_STORE_CHANGE); return EFI_NO_MEDIA; } if (*(UINT64 *) (ZeroPageBitMap) != RapidStartData->RapidStartStoreUid) { DEBUG ((EFI_D_ERROR, "\nRapidStart Store UID Mismatched! Aborted RapidStart resume.\n")); RapidStartSetFlag (RAPID_START_FLAG_STORE_CHANGE); return EFI_NO_MEDIA; } } ZeroPageBitMap += 2; DEBUG ((EFI_D_INFO, "Transfer ranges:\n")); PrintRanges (MemRanges); RETURN_ON_ERROR (HashingInit (&Hashing, SmRamRanges)); DataCount = 0; HashingDone = FALSE; AtaCmd.Lba = LbaForData + MEM_TO_SECT (SumRanges (SmRamRanges)); Range = MemRanges; SmRamBufferLba = AtaCmd.Lba; /// /// Log the last Lba which not in used. /// Instance->NotUsedLba = AtaCmd.Lba; PERF_START_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2510); while (Range->Start != END_RANGES) { AtaCmd.MemAddr = Range->Start; SectorsLeft = MEM_TO_SECT (RANGE_LENGTH (Range)); while (SectorsLeft > 0) { if (HashingDone || AhciHasFreeCmdSlot (Ahci)) { if (SectorsLeft > AHCI_MAX_SECTORS) { AtaCmd.SectorCount = AHCI_MAX_SECTORS; } else { AtaCmd.SectorCount = SectorsLeft; } SectorsLeft -= AtaCmd.SectorCount; RETURN_ON_ERROR (AhciPostCommandWithZeroFilter (Ahci, &AtaCmd, NULL, ZeroPageBitMap, &SmRamBufferLba)); DataCount += AtaCmd.SectorCount << SECTOR_SHIFT; } else { if (Ahci->PollCancellation && RapidStartShouldCancelEntry ()) { return EFI_NOT_STARTED; } HashingDone = HashingProcessChunk (&Hashing); } /// /// Log the last Lba which not in used. /// Instance->NotUsedLba = AtaCmd.Lba; } ++Range; } RETURN_ON_ERROR (HashingCompleteAndVerify (&Hashing, Instance)); PERF_END_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2511); if (Transition == RapidStartEntry) { Ahci->PollCancellation = FALSE; AtaCmd.Lba = LbaForData; RETURN_ON_ERROR (RapidStartHandleSmRam (Ahci, &AtaCmd, Transition, SmRamRanges)); /// /// Restore LEGACY_SMRAM_BUFFER /// AtaCmd.Command = ATA_CMD_READ_DMA_EXT; AtaCmd.Direction = AHCI_DIR_DEV2HOST; AtaCmd.MemAddr = LEGACY_SMRAM_BUFFER; AtaCmd.SectorCount = MEM_TO_SECT (LEGACY_SMRAM_SIZE); AtaCmd.Lba = SmRamBufferLba; RETURN_ON_ERROR (AhciPostCommandWithZeroFilter (Ahci, &AtaCmd, NULL, ZeroPageBitMap, NULL)); } /// /// If LEGACY_SMRAM_BUFFER contained zero pages, they have to be zero-ed /// ZeroBitmapForSmramBuffer = *(ZeroPageBitMap + (LEGACY_SMRAM_BUFFER / EFI_PAGE_SIZE / 8 / 4)); SmramBuffer = (UINT32 *) LEGACY_SMRAM_BUFFER; if (ZeroBitmapForSmramBuffer != 0) { while (SmramBuffer < (UINT32 *) (LEGACY_SMRAM_BUFFER + LEGACY_SMRAM_SIZE)) { if (ZeroBitmapForSmramBuffer & 1) { ZeroMem (SmramBuffer, EFI_PAGE_SIZE); } ZeroBitmapForSmramBuffer >>= 1; SmramBuffer += (EFI_PAGE_SIZE / 4); } } PERF_START_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2520); Status = AhciWaitAllComplete (Ahci); PERF_END_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2521); #ifdef RAPID_START_WHOLE_MEMORY_CHECK /// /// Verify CRC32 for memory below 4GB /// if (Transition == RapidStartExit) { SaveOrCompareCrc32 (TRUE, 0, RapidStartData->Tolm, RapidStartData); } #endif return Status; } /** Save/Restore memory content to/from RapidStart Store @param[in] Instance - RapidStart instance @retval EFI_SUCCESS - RapidStart transition succeeded @retval EFI_BAD_BUFFER_SIZE - Memory configuration changed. Abort RapidStart transition. @retval EFI_BUFFER_TOO_SMALL - RapidStart Store size is too small to store memory content @retval EFI_TIMEOUT - AHCI timeout occured @retval EFI_DEVICE_ERROR - AHCI device not ready **/ STATIC EFI_STATUS RapidStartTransitionInternal ( IN RAPID_START_INSTANCE *Instance ) { RAPID_START_PERSISTENT_DATA *RapidStartData; AHCI_CONTEXT *Ahci; UINTN MchBase; UINT8 OldSmramControl; UINT32 OldTseg; UINT8 OldPam[PAM_COUNT]; MEMORY_RANGE *MemRanges; MEMORY_RANGE SmRamRanges[MAX_SMRAM_RANGES + 1]; UINTN Index; EFI_STATUS Status; UINT32 OldDpr; MemRanges = (MEMORY_RANGE *) AllocatePool (sizeof(MEMORY_RANGE) * (MAX_MEMORY_RANGES + 1)); if (MemRanges == NULL) { DEBUG ((EFI_D_ERROR, "Failed to allocate memory for MemRanges! \n")); return EFI_OUT_OF_RESOURCES; } RapidStartData = &Instance->Data; Ahci = &Instance->Ahci; #ifdef EFI_DEBUG DumpMcRegisters (); #endif DEBUG ((EFI_D_INFO, "Mem : %x\n", (UINT32) RapidStartData->RapidStartMem)); DEBUG ((EFI_D_INFO, "Io : %x\n", (UINT32) RapidStartData->MmioSpace)); DEBUG ( (EFI_D_INFO, "Store: port=%d start=%lx size=%x\n", RapidStartData->StoreSataPort, RapidStartData->StoreLbaAddr, RapidStartData->StoreSectors) ); MchBase = MmPciAddress (0, SA_MC_BUS, SA_MC_DEV, SA_MC_FUN, 0); if (RapidStartData->Tolm != (MmioRead32 (MchBase + R_SA_BDSM) & B_SA_BDSM_BDSM_MASK)) { DEBUG ((EFI_D_ERROR, "Memory Tolm changed\n")); return EFI_BAD_BUFFER_SIZE; } if (RapidStartData->Tohm != (MmioRead64 (MchBase + R_SA_TOUUD) & B_SA_TOUUD_TOUUD_MASK)) { DEBUG ((EFI_D_ERROR, "Memory Tohm changed\n")); return EFI_BAD_BUFFER_SIZE; } DEBUG ((EFI_D_INFO, "Total Memory: %lx\n", RapidStartData->TotalMem)); Status = GetSmRamRanges (SmRamRanges, VARIABLE_SIZE (SmRamRanges), Instance->IedSize); ASSERT_EFI_ERROR (Status); DEBUG ((EFI_D_INFO, "SMRAM ranges:\n")); PrintRanges (SmRamRanges); Status = BuildMemoryRanges ( MemRanges, VARIABLE_SIZE (MemRanges), RapidStartData ); ASSERT_EFI_ERROR (Status); DEBUG ((EFI_D_INFO, "Memory ranges:\n")); PrintRanges (MemRanges); /// /// Save and open SMRAM /// OldSmramControl = MmioRead8 (MchBase + R_SA_SMRAMC); MmioWrite8 (MchBase + R_SA_SMRAMC, B_SA_SMRAMC_D_OPEN_MASK | B_SA_SMRAMC_G_SMRAME_MASK); /// /// Open PAM regions /// for (Index = 0; Index < PAM_COUNT; ++Index) { OldPam[Index] = MmioRead8 (MchBase + R_SA_PAM0 + Index); MmioWrite8 (MchBase + R_SA_PAM0 + Index, B_SA_PAM1_HIENABLE_MASK | B_SA_PAM1_LOENABLE_MASK); } /// /// Disable TSEG and DPR /// OldTseg = MmioRead32 (MchBase + R_SA_TSEGMB); MmioWrite32 (MchBase + R_SA_TSEGMB, RapidStartData->Tolm); OldDpr = MmioRead32 (MchBase + R_SA_DPR); MmioAnd32 (MchBase + R_SA_DPR, (UINT32) ~B_SA_DPR_EPM_MASK); PERF_START_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2500); Status = AhciPortInit (Ahci); PERF_END_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2501); // // Unlock device if password locked // PERF_START_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2530); if (Status == EFI_SUCCESS) { Status = AhciGetLockStatus (Ahci); if (Status == EFI_ACCESS_DENIED) { if (Instance->Transition == RapidStartEntry) { Instance->PwdStatus = RapidStartGetDriveUnlockPassword ( GetPeiServicesTablePointer (), Instance->Transition, Instance->Data.StoreSataPort, (UINT8 *) Instance->HddPassword, &Instance->FreezeLock ); } if (Instance->PwdStatus == EFI_SUCCESS) { Status = AhciSecurityUnlock (Ahci, Instance->HddPassword); if (Instance->FreezeLock) { AhciSimpleCommand (Ahci, ATA_CMD_SECURITY_FREEZE_LOCK); } } else { DEBUG ((EFI_D_ERROR, "RapidStart: No drive password provided!")); } } } ZeroMem (Instance->HddPassword, ATA_PASSWORD_LEN); PERF_END_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2531); if (Status == EFI_SUCCESS) { // // DPR will be disable temporary, so we won't exculde this region. // To save/restore DPR region as normal memory does. // Instance->NotUsedLba = RapidStartData->StoreLbaAddr; PERF_START_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2540); Status = RapidStartMemoryDiskTransfer (Instance, MemRanges, SmRamRanges); PERF_END_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2541); if ((Instance->Transition == RapidStartEntry) && (Status == EFI_SUCCESS)) { AhciSimpleCommand (Ahci, ATA_CMD_FLUSH_CACHE); AhciSimpleCommand (Ahci, ATA_CMD_STANDBY_IMMEDIATE); AhciPortDone (Ahci); ForEachAhciPort (Ahci, StandbyAhciPort, AhciGetPresentPorts () & ~(1u << Ahci->Port)); } else { /// /// 1. In RapidStart Entry, if user has canceled RapidStart Entry flow, TRIM RapidStart Store. /// 2. In RapidStart Resume, only TRIM when Resume successfully, SMRAM content changed. /// #ifdef RAPID_START_TRIM_ON_RESUME if (((Instance->Transition == RapidStartEntry) && (Status == EFI_NOT_STARTED)) || ((Instance->Transition == RapidStartExit) && ((Status == EFI_SUCCESS) || (Status == EFI_SECURITY_VIOLATION))) ) { DEBUG ((EFI_D_INFO, "\nNot used LBA in RapidStart store = %lX\n", Instance->NotUsedLba)); ASSERT (Instance->NotUsedLba > RapidStartData->StoreLbaAddr); Ahci->PollCancellation = FALSE; PERF_START_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2550); AhciTrimRange ( Ahci, RapidStartData->StoreLbaAddr, (UINT32) (Instance->NotUsedLba - RapidStartData->StoreLbaAddr) ); PERF_END_EX (NULL, L"EventRec", NULL, AsmReadTsc (), 0x2551); } #endif } AhciPortDone (Ahci); } AhciDone (Ahci); // // Restore TSEG // MmioWrite32 (MchBase + R_SA_TSEGMB, OldTseg); MmioWrite32 (MchBase + R_SA_DPR, OldDpr); // // Restore PAM regions // for (Index = 0; Index < PAM_COUNT; ++Index) { MmioWrite8 (MchBase + R_SA_PAM0 + Index, OldPam[Index]); } // // Restore SMRAMC // MmioWrite8 (MchBase + R_SA_SMRAMC, OldSmramControl); return Status; } /** Restores Memory pointers required by MemoryInit to install S3 reserved area on S3 resume. @param[in] This - RapidStart PPI **/ STATIC VOID RapidStartRestoreS3Pointers ( IN RAPID_START_PPI *This ) { RAPID_START_INSTANCE *Instance; RAPID_START_PERSISTENT_DATA *RapidStartData; UINT64 AcpiVariableSet64; ACPI_VARIABLE_SET *AcpiVariableSet; EFI_PEI_READ_ONLY_VARIABLE_PPI *ReadOnlyVariable; UINTN VarSize; EFI_STATUS Status; DEBUG ((EFI_D_INFO, "RapidStartRestoreS3Pointers\n")); Instance = RAPID_START_INSTANCE_FROM_THIS (This); RapidStartData = &Instance->Data; Status = PeiServicesLocatePpi (&gEfiPeiReadOnlyVariablePpiGuid, 0, NULL, (VOID **) &ReadOnlyVariable); ASSERT_EFI_ERROR (Status); VarSize = sizeof (AcpiVariableSet64); Status = ReadOnlyVariable->PeiGetVariable ( GetPeiServicesTablePointer (), ACPI_GLOBAL_VARIABLE, &gEfiAcpiVariableGuid, NULL, &VarSize, &AcpiVariableSet64 ); AcpiVariableSet = (ACPI_VARIABLE_SET *) (UINTN) AcpiVariableSet64; ASSERT (!EFI_ERROR (Status) && AcpiVariableSet != NULL); if (EFI_ERROR (Status) || AcpiVariableSet == NULL) { return; } #ifdef RAPID_START_USE_OLD_ACPI_VARIABLE_SET AcpiVariableSet->SystemMemoryLength = RapidStartData->SystemMemoryLengthBelow4GB; #else AcpiVariableSet->SystemMemoryLengthBelow4GB = RapidStartData->SystemMemoryLengthBelow4GB; AcpiVariableSet->SystemMemoryLengthAbove4GB = RapidStartData->SystemMemoryLengthAbove4GB; #endif AcpiVariableSet->AcpiReservedMemoryBase = RapidStartData->AcpiReservedMemoryBase; AcpiVariableSet->AcpiReservedMemorySize = RapidStartData->AcpiReservedMemorySize; DEBUG ((EFI_D_INFO, "SystemMemoryLengthBelow4GB %lx\n", RapidStartData->SystemMemoryLengthBelow4GB)); DEBUG ((EFI_D_INFO, "SystemMemoryLengthAbove4GB %lx\n", RapidStartData->SystemMemoryLengthAbove4GB)); DEBUG ((EFI_D_INFO, "AcpiReservedMemoryBase %lx\n", RapidStartData->AcpiReservedMemoryBase)); DEBUG ((EFI_D_INFO, "AcpiReservedMemorySize %x\n", RapidStartData->AcpiReservedMemorySize)); } /** Check if RapidStart support is enabled. @param[in] This - Pointer to RapidStart PPI @retval TRUE if RapidStart support is enabled FALSE otherwise **/ BOOLEAN RapidStartIsEnabled ( IN RAPID_START_PPI *This ) { RAPID_START_INSTANCE *Instance; Instance = RAPID_START_INSTANCE_FROM_THIS (This); return Instance->Enabled; } /** Detremines RapidStart transition. @param[in] This - Pointer to RapidStart PPI @retval RapidStart_TRANSITION - The RapidStart transition being performed **/ RAPID_START_TRANSITION RapidStartGetMode ( IN RAPID_START_PPI *This ) { RAPID_START_INSTANCE *Instance; DEBUG ((EFI_D_INFO, "RapidStartGetMode()\n")); Instance = RAPID_START_INSTANCE_FROM_THIS (This); ASSERT (Instance->Transition != RapidStartTransitionMax); return Instance->Transition; } /** Performs actual RapidStart transition. @param[in] This - RapidStart PPI @retval EFI_ABORTED - RapidStart Entry or Resume failed @retval EFI_SUCCESS - RapidStart Entry/Resume successfully **/ STATIC EFI_STATUS RapidStartDoTransition ( IN RAPID_START_PPI *This ) { RAPID_START_INSTANCE *Instance; RAPID_START_PERSISTENT_DATA *RapidStartData; RAPID_START_MEM_DATA *RapidStartMemData; EFI_PEI_SERVICES **PeiServices; EFI_STATUS Status; EFI_STATUS MeStatus; UINT32 PmBase; /// /// 1857 = Intel Rapid Start Technology /// IoWrite16 (0x80, 0x1857); DEBUG ((EFI_D_INFO, "RapidStartDoTransition()\n")); PmBase = MmioRead32 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_ACPI_BASE) ) & B_PCH_LPC_ACPI_BASE_BAR; PeiServices = GetPeiServicesTablePointer (); Instance = RAPID_START_INSTANCE_FROM_THIS (This); RapidStartData = &Instance->Data; RapidStartMemData = RAPID_START_MEM_DATA_PTR (RapidStartData); Status = RapidStartBeforeTransition ( GetPeiServicesTablePointer (), Instance->Transition, Instance->Data.StoreSataPort ); if (EFI_ERROR (Status)) { Instance->Transition = RapidStartNone; } switch (Instance->Transition) { case RapidStartEntry: DEBUG ((EFI_D_ERROR, "RapidStart ENTRY\n")); /// /// Clear all GPE events before SIM enabling /// RapidStartClearAndEnablePmeEvent (FALSE); RapidStartMemData->EntryCanceled = 0; Status = RapidStartTransitionInternal (Instance); MeStatus = EFI_SUCCESS; MeStatus = WaitMeInitDone (); if (RapidStartMemData->OsWakeTimeEnabled && (Status != EFI_NOT_STARTED)) { DEBUG ( (EFI_D_INFO, "Setting RTC alarm to OS provided value %02d-%02d:%02d:%02d\n", RapidStartMemData->OsWakeTime.Date, RapidStartMemData->OsWakeTime.Hour, RapidStartMemData->OsWakeTime.Minute, RapidStartMemData->OsWakeTime.Second) ); RtcSetAlarm (&RapidStartMemData->OsWakeTime); // // set RTC_EN bit in PM1_EN to wake up from the alarm // IoWrite16 ( PmBase + R_PCH_ACPI_PM1_EN, (IoRead16 (PmBase + R_PCH_ACPI_PM1_EN) | B_PCH_ACPI_PM1_EN_RTC) ); } if (EFI_ERROR (Status) || EFI_ERROR (MeStatus)) { if (Status == EFI_NOT_STARTED) { /// /// RapidStart Entry flow has been canceled, do S3 resume. /// DEBUG ((EFI_D_ERROR, "User canceled RapidStart!\n")); RapidStartMemData->EntryCanceled = 1; } else { DEBUG ((EFI_D_ERROR, "RapidStart entry failed!\n")); Instance->RapidStartGlobalNvs->EventsEnabled = 0; Instance->RapidStartGlobalNvs->EventsAvailable = 0; } Status = PeiServicesNotifyPpi (&mRapidStartRecoveryNotifyDesc); ASSERT_EFI_ERROR (Status); RapidStartAfterTransition (PeiServices, RapidStartEntry, Status, RapidStartData->StoreSataPort); break; } RapidStartSetFlag (RAPID_START_FLAG_ENTRY_DONE); RapidStartAfterTransition (PeiServices, RapidStartEntry, Status, RapidStartData->StoreSataPort); Status = PeiServicesNotifyPpi (&mRapidStartS4NotifyDesc); ASSERT_EFI_ERROR (Status); break; case RapidStartExit: DEBUG ((EFI_D_ERROR, "RapidStart EXIT\n")); Status = PeiServicesNotifyPpi (&mRapidStartExitNotifyDesc); ASSERT_EFI_ERROR (Status); Status = RapidStartTransitionInternal (Instance); MeStatus = EFI_SUCCESS; if (EFI_ERROR (Status) || EFI_ERROR (MeStatus)) { DEBUG ((EFI_D_ERROR, "RapidStart exit failed!\n")); if (EFI_ERROR (Status)) { RapidStartAfterTransition (PeiServices, RapidStartExit, Status, RapidStartData->StoreSataPort); } if (EFI_ERROR (MeStatus)) { RapidStartAfterTransition (PeiServices, RapidStartExit, MeStatus, RapidStartData->StoreSataPort); } PeiServicesResetSystem (); EFI_DEADLOOP (); } /// /// Set DISB /// MmioOr16 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_GEN_PMCON_2), B_PCH_LPC_GEN_PMCON_DRAM_INIT ); RapidStartAfterTransition (PeiServices, RapidStartExit, EFI_SUCCESS, RapidStartData->StoreSataPort); break; case RapidStartNone: /// /// Cancelled /// Status = EFI_ABORTED; break; default: ASSERT (0); } IoWrite16 (0x80, 0); return Status; } /** Executes or schedules RapidStart transition if appropriate. @param[in] This - Pointer to RapidStart PPI @param[out] BootMode - Boot mode that may be overridden @retval EFI_SUCCESS - RapidStart transition performed/scheduled successfully @retval EFI_ABORTED - RapidStart transition aborted **/ STATIC EFI_STATUS RapidStartTransitionEntryPoint ( IN RAPID_START_PPI *This, OUT EFI_BOOT_MODE *BootMode ) { EFI_STATUS Status; RAPID_START_TRANSITION Transition; DEBUG ((EFI_D_INFO, "RapidStartTransitionEntryPoint\n")); Transition = RapidStartGetMode (This); Status = EFI_SUCCESS; if (Transition != RapidStartNone) { #ifdef RAPID_START_ON_MEMORY_INSTALLED RapidStartRestoreS3Pointers (This); Status = PeiServicesNotifyPpi (&mRapidStartTransitionPpiNotifyDesc); ASSERT_EFI_ERROR (Status); #else Status = RapidStartDoTransition (This); #endif if (Transition == RapidStartExit && BootMode != NULL) { *BootMode = BOOT_ON_S3_RESUME; /// /// Update Boot Mode /// Status = PeiServicesSetBootMode (BOOT_ON_S3_RESUME); ASSERT_EFI_ERROR (Status); } } return Status; } /** Checks power button override status. @retval TRUE if power button override has taken place, FALSE otherwise. **/ STATIC BOOLEAN IsPowerButtonOverride ( VOID ) { UINT32 PmBase; PmBase = MmioRead32 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_ACPI_BASE) ) & B_PCH_LPC_ACPI_BASE_BAR; return (IoRead16 (PmBase + R_PCH_ACPI_PM1_STS) & B_PCH_ACPI_PM1_STS_PRBTNOR) != 0; } /** Retrieves RapidStart non-volatile flag and data, initializes RapidStart instance, handles RapidStart entry conditions and prerequisites. @param[in,out] Instance - RapidStart instance **/ STATIC VOID RapidStartInitInstance ( IN OUT RAPID_START_INSTANCE *Instance ) { EFI_PEI_READ_ONLY_VARIABLE_PPI *ReadOnlyVariable; UINTN Size; EFI_STATUS Status; BOOLEAN RapidStartFlag; UINT8 EventsEnabled; UINT32 PmBase; EFI_BOOT_MODE BootMode; SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi; Instance->Signature = RAPID_START_INSTANCE_SIGNATURE; Instance->Transition = RapidStartNone; Instance->Enabled = FALSE; Instance->FreezeLock = TRUE; Instance->PwdStatus = EFI_NOT_FOUND; Status = PeiServicesLocatePpi (&gEfiPeiReadOnlyVariablePpiGuid, 0, NULL, (VOID **) &ReadOnlyVariable); ASSERT_EFI_ERROR (Status); Size = sizeof (RAPID_START_PERSISTENT_DATA); Status = ReadOnlyVariable->PeiGetVariable ( GetPeiServicesTablePointer (), gRapidStartPersistentDataName, &gRapidStartPersistentDataGuid, NULL, &Size, &Instance->Data ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_INFO, "RapidStart disabled (No RapidStart variable found)\n")); return ; } Instance->Enabled = TRUE; Instance->RapidStartGlobalNvs = (RAPID_START_GLOBAL_NVS_AREA *) Instance->Data.RapidStartGlobalNvsPtr; DEBUG ((EFI_D_INFO, "RapidStartGlobalNVS: %x\n", Instance->RapidStartGlobalNvs)); Status = PeiServicesGetBootMode (&BootMode); ASSERT_EFI_ERROR (Status); // // Get IED size through the SaPlatformPolicy PPI // SaPlatformPolicyPpi = NULL; Status = PeiServicesLocatePpi ( &gSaPlatformPolicyPpiGuid, 0, NULL, (VOID **) &SaPlatformPolicyPpi ); ASSERT_EFI_ERROR (Status); if ((SaPlatformPolicyPpi != NULL) && (SaPlatformPolicyPpi->Revision >= SA_PLATFORM_POLICY_PPI_REVISION_9)) { Instance->IedSize = SaPlatformPolicyPpi->PlatformData->IedSize; } else { Instance->IedSize = 0x400000; } if (BootMode == BOOT_ON_S3_RESUME) { DEBUG ((EFI_D_INFO, "S3 resume\n")); PmBase = MmioRead32 ( MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPC, PCI_FUNCTION_NUMBER_PCH_LPC, R_PCH_LPC_ACPI_BASE) ) & B_PCH_LPC_ACPI_BASE_BAR; Status = RapidStartGetConfig (&EventsEnabled); ASSERT_EFI_ERROR (Status); /// /// Check RTC alarm wake event /// if (IoRead16 (PmBase + R_PCH_ACPI_PM1_STS) & B_PCH_ACPI_PM1_STS_RTC) { /// /// Clear RTC Alarm Flag /// RtcRead (R_PCH_RTC_REGC); if (EventsEnabled & RAPID_START_ACPI_RTC_WAKE) { DEBUG ((EFI_D_INFO, "RapidStart on RTC wake\n")); Instance->Transition = RapidStartEntry; } else { DEBUG ((EFI_D_INFO, "Boot from RapidStart cancellation or this is waking by OS timer\n")); } } /// /// Check for critical battery wake event /// else if ((EventsEnabled & RAPID_START_ACPI_BATT_WAKE) && RapidStartCheckCriticalBatteryWakeupEvent ()) { DEBUG ((EFI_D_INFO, "RapidStart on critical battery wake from EC.\n")); RapidStartInitializeCriticalBatteryWakeupEvent (FALSE); RapidStartClearAllKscWakeStatus (); Instance->Transition = RapidStartEntry; } return ; } /// /// Check RapidStart non-volatile flag /// Status = RapidStartGetFlag (&RapidStartFlag); if (EFI_ERROR (Status) || ((RapidStartFlag & RAPID_START_FLAG_ENTRY_DONE) == 0)) { DEBUG ((EFI_D_INFO, "No RapidStart flag set\n")); return ; } /// /// RapidStart exit path (Cold boot assumed) /// For security reasones need to clear SMRAM hash and drive password from non-volatile /// memory first. /// #ifndef RAPID_START_NO_SMRAM_INTEGRITY_CHECK Status = RapidStartRestoreAndClearSecureHash (Instance->SmRamHash); #endif Instance->PwdStatus = RapidStartGetDriveUnlockPassword ( GetPeiServicesTablePointer (), RapidStartExit, Instance->Data.StoreSataPort, (UINT8 *) Instance->HddPassword, &Instance->FreezeLock ); /// /// This is either RapidStart Resume flow or non-RapidStart transition /// Clear RapidStart flag in the start point. /// RapidStartSetFlag (0); if (EFI_ERROR (Status)) { return ; } if (IsPowerButtonOverride ()) { DEBUG ((EFI_D_INFO, "PWRBTN override\n")); return ; } DEBUG ((EFI_D_INFO, "RapidStart exit\n")); Instance->Transition = RapidStartExit; } /** Install RapiStartPpi. @param[in] PeiServices - Pointer to PEI Services Table. @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that caused this function to execute. @param[in] Ppi - Pointer to the PPI data associated with this function. @retval EFI_SUCCESS **/ STATIC EFI_STATUS InstallRapidStartPpi ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, IN VOID *Ppi ) { RAPID_START_INSTANCE *Instance; EFI_STATUS Status; Instance = AllocateZeroPool (sizeof (RAPID_START_INSTANCE)); ASSERT (Instance != NULL); if (Instance == NULL) { return EFI_OUT_OF_RESOURCES; } Instance->Ppi.Revision = RAPID_START_PPI_REVISION_1; Instance->Ppi.GetMode = RapidStartGetMode; Instance->Ppi.TransitionEntryPoint = RapidStartTransitionEntryPoint; Instance->Ppi.IsEnabled = RapidStartIsEnabled; Instance->PpiDesc.Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST; Instance->PpiDesc.Guid = &gRapidStartPpiGuid; Instance->PpiDesc.Ppi = &Instance->Ppi; RapidStartInitInstance (Instance); if (Instance->Transition != RapidStartNone) { RapidStartInitAhci (Instance); } if (Instance->Transition == RapidStartEntry) { ForEachAhciPort ( &Instance->Ahci, AhciSpinUpPort, AhciGetEnabledPorts () & ~(1u << Instance->Ahci.Port) ); } Status = PeiServicesInstallPpi (&Instance->PpiDesc); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } /** RapidStart PEIM entry point driver @param[in] FfsHeader - FFS file header pointer of this driver @param[in] PeiServices - Pointer to PEI service table @retval EFI_SUCCESS - RapidStart PPI installed successfully @retval EFI_OUT_OF_RESOURCES - Error in allocate required resource. **/ EFI_STATUS RapidStartPeiEntryPoint ( IN EFI_FFS_FILE_HEADER *FfsHeader, IN EFI_PEI_SERVICES **PeiServices ) { PEI_HECI_PPI *HeciPpi; EFI_STATUS Status; DEBUG ((EFI_D_INFO, "RapidStart: RapidStartPeiEntryPoint\n")); /// /// Install RapiStart Ppi when Heci Ppi is ready. /// Status = (*PeiServices)->LocatePpi ( PeiServices, &gPeiHeciPpiGuid, 0, NULL, (VOID **) &HeciPpi ); if (EFI_ERROR(Status)) { Status = PeiServicesNotifyPpi (&mInstallRapidStartPpiNotifyDesc); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } return InstallRapidStartPpi (PeiServices, NULL, NULL); }