diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 74f6e784f3..7e01b21e28 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -421,6 +421,12 @@ PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf } +!if $(SMM_REQUIRE) == TRUE + OvmfPkg/SmmAccess/SmmAccessPei.inf { + + PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf + } +!endif # # DXE Phase modules diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index 30bd3bc79f..dcdc4e1123 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -171,6 +171,9 @@ INF IntelFrameworkModulePkg/Universal/StatusCode/Pei/StatusCodePei.inf INF OvmfPkg/PlatformPei/PlatformPei.inf INF MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf INF UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf +!if $(SMM_REQUIRE) == TRUE +INF OvmfPkg/SmmAccess/SmmAccessPei.inf +!endif ################################################################################ diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index cdf4e0cb90..81391ae699 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -427,6 +427,12 @@ PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf } +!if $(SMM_REQUIRE) == TRUE + OvmfPkg/SmmAccess/SmmAccessPei.inf { + + PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf + } +!endif [Components.X64] # diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index 59c3362950..d28cf7dab1 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -171,6 +171,9 @@ INF IntelFrameworkModulePkg/Universal/StatusCode/Pei/StatusCodePei.inf INF OvmfPkg/PlatformPei/PlatformPei.inf INF MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf INF UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf +!if $(SMM_REQUIRE) == TRUE +INF OvmfPkg/SmmAccess/SmmAccessPei.inf +!endif ################################################################################ diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 2b0ad9d05d..2a39878b30 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -426,6 +426,12 @@ PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf } +!if $(SMM_REQUIRE) == TRUE + OvmfPkg/SmmAccess/SmmAccessPei.inf { + + PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf + } +!endif # # DXE Phase modules diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index 6ba1948faa..a4dbbfd13a 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -171,6 +171,9 @@ INF IntelFrameworkModulePkg/Universal/StatusCode/Pei/StatusCodePei.inf INF OvmfPkg/PlatformPei/PlatformPei.inf INF MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf INF UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf +!if $(SMM_REQUIRE) == TRUE +INF OvmfPkg/SmmAccess/SmmAccessPei.inf +!endif ################################################################################ diff --git a/OvmfPkg/SmmAccess/SmmAccessPei.c b/OvmfPkg/SmmAccess/SmmAccessPei.c new file mode 100644 index 0000000000..a4ce610a46 --- /dev/null +++ b/OvmfPkg/SmmAccess/SmmAccessPei.c @@ -0,0 +1,393 @@ +/** @file + + A PEIM with the following responsibilities: + + - verify & configure the Q35 TSEG in the entry point, + - provide SMRAM access by producing PEI_SMM_ACCESS_PPI, + - set aside the SMM_S3_RESUME_STATE object at the bottom of TSEG, and expose + it via the gEfiAcpiVariableGuid GUID HOB. + + This PEIM runs from RAM, so we can write to variables with static storage + duration. + + Copyright (C) 2013, 2015, Red Hat, Inc.
+ Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "SmramInternal.h" + +// +// PEI_SMM_ACCESS_PPI implementation. +// + +/** + Opens the SMRAM area to be accessible by a PEIM driver. + + This function "opens" SMRAM so that it is visible while not inside of SMM. + The function should return EFI_UNSUPPORTED if the hardware does not support + hiding of SMRAM. The function should return EFI_DEVICE_ERROR if the SMRAM + configuration is locked. + + @param PeiServices General purpose services available to every + PEIM. + @param This The pointer to the SMM Access Interface. + @param DescriptorIndex The region of SMRAM to Open. + + @retval EFI_SUCCESS The region was successfully opened. + @retval EFI_DEVICE_ERROR The region could not be opened because locked + by chipset. + @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. + +**/ +STATIC +EFI_STATUS +EFIAPI +SmmAccessPeiOpen ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN UINTN DescriptorIndex + ) +{ + if (DescriptorIndex >= DescIdxCount) { + return EFI_INVALID_PARAMETER; + } + + // + // According to current practice, DescriptorIndex is not considered at all, + // beyond validating it. + // + return SmramAccessOpen (&This->LockState, &This->OpenState); +} + +/** + Inhibits access to the SMRAM. + + This function "closes" SMRAM so that it is not visible while outside of SMM. + The function should return EFI_UNSUPPORTED if the hardware does not support + hiding of SMRAM. + + @param PeiServices General purpose services available to every + PEIM. + @param This The pointer to the SMM Access Interface. + @param DescriptorIndex The region of SMRAM to Close. + + @retval EFI_SUCCESS The region was successfully closed. + @retval EFI_DEVICE_ERROR The region could not be closed because + locked by chipset. + @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. + +**/ +STATIC +EFI_STATUS +EFIAPI +SmmAccessPeiClose ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN UINTN DescriptorIndex + ) +{ + if (DescriptorIndex >= DescIdxCount) { + return EFI_INVALID_PARAMETER; + } + + // + // According to current practice, DescriptorIndex is not considered at all, + // beyond validating it. + // + return SmramAccessClose (&This->LockState, &This->OpenState); +} + +/** + Inhibits access to the SMRAM. + + This function prohibits access to the SMRAM region. This function is usually + implemented such that it is a write-once operation. + + @param PeiServices General purpose services available to every + PEIM. + @param This The pointer to the SMM Access Interface. + @param DescriptorIndex The region of SMRAM to Close. + + @retval EFI_SUCCESS The region was successfully locked. + @retval EFI_DEVICE_ERROR The region could not be locked because at + least one range is still open. + @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. + +**/ +STATIC +EFI_STATUS +EFIAPI +SmmAccessPeiLock ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN UINTN DescriptorIndex + ) +{ + if (DescriptorIndex >= DescIdxCount) { + return EFI_INVALID_PARAMETER; + } + + // + // According to current practice, DescriptorIndex is not considered at all, + // beyond validating it. + // + return SmramAccessLock (&This->LockState, &This->OpenState); +} + +/** + Queries the memory controller for the possible regions that will support + SMRAM. + + @param PeiServices General purpose services available to every + PEIM. + @param This The pointer to the SmmAccessPpi Interface. + @param SmramMapSize The pointer to the variable containing size of + the buffer to contain the description + information. + @param SmramMap The buffer containing the data describing the + Smram region descriptors. + + @retval EFI_BUFFER_TOO_SMALL The user did not provide a sufficient buffer. + @retval EFI_SUCCESS The user provided a sufficiently-sized buffer. + +**/ +STATIC +EFI_STATUS +EFIAPI +SmmAccessPeiGetCapabilities ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN OUT UINTN *SmramMapSize, + IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap + ) +{ + return SmramAccessGetCapabilities (This->LockState, This->OpenState, + SmramMapSize, SmramMap); +} + +// +// LockState and OpenState will be filled in by the entry point. +// +STATIC PEI_SMM_ACCESS_PPI mAccess = { + &SmmAccessPeiOpen, + &SmmAccessPeiClose, + &SmmAccessPeiLock, + &SmmAccessPeiGetCapabilities +}; + + +STATIC EFI_PEI_PPI_DESCRIPTOR mPpiList[] = { + { + EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gPeiSmmAccessPpiGuid, &mAccess + } +}; + + +// +// Utility functions. +// +STATIC +UINT8 +CmosRead8 ( + IN UINT8 Index + ) +{ + IoWrite8 (0x70, Index); + return IoRead8 (0x71); +} + +STATIC +UINT32 +GetSystemMemorySizeBelow4gb ( + VOID + ) +{ + UINT32 Cmos0x34; + UINT32 Cmos0x35; + + Cmos0x34 = CmosRead8 (0x34); + Cmos0x35 = CmosRead8 (0x35); + + return ((Cmos0x35 << 8 | Cmos0x34) << 16) + SIZE_16MB; +} + + +// +// Entry point of this driver. +// +EFI_STATUS +EFIAPI +SmmAccessPeiEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + UINT16 HostBridgeDevId; + UINT8 EsmramcVal; + UINT8 RegMask8; + UINT32 TopOfLowRam, TopOfLowRamMb; + EFI_STATUS Status; + UINTN SmramMapSize; + EFI_SMRAM_DESCRIPTOR SmramMap[DescIdxCount]; + VOID *GuidHob; + + // + // This module should only be included if SMRAM support is required. + // + ASSERT (FeaturePcdGet (PcdSmmSmramRequire)); + + // + // Verify if we're running on a Q35 machine type. + // + HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID); + if (HostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) { + DEBUG ((EFI_D_ERROR, "%a: no SMRAM with host bridge DID=0x%04x; only " + "DID=0x%04x (Q35) is supported\n", __FUNCTION__, HostBridgeDevId, + INTEL_Q35_MCH_DEVICE_ID)); + goto WrongConfig; + } + + // + // Confirm if QEMU supports SMRAM. + // + // With no support for it, the ESMRAMC (Extended System Management RAM + // Control) register reads as zero. If there is support, the cache-enable + // bits are hard-coded as 1 by QEMU. + // + EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)); + RegMask8 = MCH_ESMRAMC_SM_CACHE | MCH_ESMRAMC_SM_L1 | MCH_ESMRAMC_SM_L2; + if ((EsmramcVal & RegMask8) != RegMask8) { + DEBUG ((EFI_D_ERROR, "%a: this Q35 implementation lacks SMRAM\n", + __FUNCTION__)); + goto WrongConfig; + } + + TopOfLowRam = GetSystemMemorySizeBelow4gb (); + ASSERT ((TopOfLowRam & (SIZE_1MB - 1)) == 0); + TopOfLowRamMb = TopOfLowRam >> 20; + + // + // Some of the following registers are no-ops for QEMU at the moment, but it + // is recommended to set them correctly, since the ESMRAMC that we ultimately + // care about is in the same set of registers. + // + // First, we disable the integrated VGA, and set both the GTT Graphics Memory + // Size and the Graphics Mode Select memory pre-allocation fields to zero. + // This takes just one write to the Graphics Control Register. + // + PciWrite16 (DRAMC_REGISTER_Q35 (MCH_GGC), MCH_GGC_IVD); + + // + // Set Top of Low Usable DRAM. + // + PciWrite16 (DRAMC_REGISTER_Q35 (MCH_TOLUD), + (UINT16)(TopOfLowRamMb << MCH_TOLUD_MB_SHIFT)); + + // + // Given the zero graphics memory sizes configured above, set the + // graphics-related stolen memory bases to the same as TOLUD. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_GBSM), + TopOfLowRamMb << MCH_GBSM_MB_SHIFT); + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_BGSM), + TopOfLowRamMb << MCH_BGSM_MB_SHIFT); + + // + // Set TSEG Memory Base. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB), + (TopOfLowRamMb - FixedPcdGet8 (PcdQ35TsegMbytes)) << MCH_TSEGMB_MB_SHIFT); + + // + // Set TSEG size, and disable TSEG visibility outside of SMM. Note that the + // T_EN bit has inverse meaning; when T_EN is set, then TSEG visibility is + // *restricted* to SMM. + // + EsmramcVal &= ~(UINT32)MCH_ESMRAMC_TSEG_MASK; + EsmramcVal |= FixedPcdGet8 (PcdQ35TsegMbytes) == 8 ? MCH_ESMRAMC_TSEG_8MB : + FixedPcdGet8 (PcdQ35TsegMbytes) == 2 ? MCH_ESMRAMC_TSEG_2MB : + MCH_ESMRAMC_TSEG_1MB; + EsmramcVal |= MCH_ESMRAMC_T_EN; + PciWrite8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), EsmramcVal); + + // + // TSEG should be closed (see above), but unlocked, initially. Set G_SMRAME + // (Global SMRAM Enable) too, as both D_LCK and T_EN depend on it. + // + PciAndThenOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM), + (UINT8)((~(UINT32)MCH_SMRAM_D_LCK) & 0xff), MCH_SMRAM_G_SMRAME); + + // + // Create the GUID HOB and point it to the first SMRAM range. + // + GetStates (&mAccess.LockState, &mAccess.OpenState); + SmramMapSize = sizeof SmramMap; + Status = SmramAccessGetCapabilities (mAccess.LockState, mAccess.OpenState, + &SmramMapSize, SmramMap); + ASSERT_EFI_ERROR (Status); + + DEBUG_CODE_BEGIN (); + { + UINTN Count; + UINTN Idx; + + Count = SmramMapSize / sizeof SmramMap[0]; + DEBUG ((EFI_D_VERBOSE, "%a: SMRAM map follows, %d entries\n", __FUNCTION__, + (INT32)Count)); + DEBUG ((EFI_D_VERBOSE, "% 20a % 20a % 20a % 20a\n", "PhysicalStart(0x)", + "PhysicalSize(0x)", "CpuStart(0x)", "RegionState(0x)")); + for (Idx = 0; Idx < Count; ++Idx) { + DEBUG ((EFI_D_VERBOSE, "% 20Lx % 20Lx % 20Lx % 20Lx\n", + SmramMap[Idx].PhysicalStart, SmramMap[Idx].PhysicalSize, + SmramMap[Idx].CpuStart, SmramMap[Idx].RegionState)); + } + } + DEBUG_CODE_END (); + + GuidHob = BuildGuidHob (&gEfiAcpiVariableGuid, + sizeof SmramMap[DescIdxSmmS3ResumeState]); + if (GuidHob == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (GuidHob, &SmramMap[DescIdxSmmS3ResumeState], + sizeof SmramMap[DescIdxSmmS3ResumeState]); + + // + // We're done. The next step should succeed, but even if it fails, we can't + // roll back the above BuildGuidHob() allocation, because PEI doesn't support + // releasing memory. + // + return PeiServicesInstallPpi (mPpiList); + +WrongConfig: + // + // We really don't want to continue in this case. + // + ASSERT (FALSE); + CpuDeadLoop (); + return EFI_UNSUPPORTED; +} diff --git a/OvmfPkg/SmmAccess/SmmAccessPei.inf b/OvmfPkg/SmmAccess/SmmAccessPei.inf new file mode 100644 index 0000000000..3908b085da --- /dev/null +++ b/OvmfPkg/SmmAccess/SmmAccessPei.inf @@ -0,0 +1,69 @@ +## @file +# A PEIM with the following responsibilities: +# +# - provide SMRAM access by producing PEI_SMM_ACCESS_PPI, +# - verify & configure the Q35 TSEG in the entry point, +# - set aside the SMM_S3_RESUME_STATE object at the bottom of TSEG, and expose +# it via the gEfiAcpiVariableGuid GUIDed HOB. +# +# Copyright (C) 2013, 2015, Red Hat, Inc. +# +# This program and the accompanying materials are licensed and made available +# under the terms and conditions of the BSD License which 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmAccessPei + FILE_GUID = 6C0E75B4-B0B9-44D1-8210-3377D7B4E066 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = SmmAccessPeiEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + SmmAccessPei.c + SmramInternal.c + SmramInternal.h + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[Guids] + gEfiAcpiVariableGuid + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + HobLib + IoLib + PcdLib + PciLib + PeiServicesLib + PeimEntryPoint + +[FeaturePcd] + gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire + +[FixedPcd] + gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes + +[Ppis] + gPeiSmmAccessPpiGuid ## PRODUCES + +[Depex] + TRUE diff --git a/OvmfPkg/SmmAccess/SmramInternal.c b/OvmfPkg/SmmAccess/SmramInternal.c new file mode 100644 index 0000000000..c3267ca940 --- /dev/null +++ b/OvmfPkg/SmmAccess/SmramInternal.c @@ -0,0 +1,188 @@ +/** @file + + Functions and types shared by the SMM accessor PEI and DXE modules. + + Copyright (C) 2015, Red Hat, Inc. + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which 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 +#include +#include +#include + +#include "SmramInternal.h" + +/** + Read the MCH_SMRAM and ESMRAMC registers, and update the LockState and + OpenState fields in the PEI_SMM_ACCESS_PPI / EFI_SMM_ACCESS2_PROTOCOL object, + from the D_LCK and T_EN bits. + + PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions can rely on + the LockState and OpenState fields being up-to-date on entry, and they need + to restore the same invariant on exit, if they touch the bits in question. + + @param[out] LockState Reflects the D_LCK bit on output; TRUE iff SMRAM is + locked. + @param[out] OpenState Reflects the inverse of the T_EN bit on output; TRUE + iff SMRAM is open. +**/ +VOID +GetStates ( + OUT BOOLEAN *LockState, + OUT BOOLEAN *OpenState +) +{ + UINT8 SmramVal, EsmramcVal; + + SmramVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_SMRAM)); + EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)); + + *LockState = !!(SmramVal & MCH_SMRAM_D_LCK); + *OpenState = !(EsmramcVal & MCH_ESMRAMC_T_EN); +} + +// +// The functions below follow the PEI_SMM_ACCESS_PPI and +// EFI_SMM_ACCESS2_PROTOCOL member declarations. The PeiServices and This +// pointers are removed (TSEG doesn't depend on them), and so is the +// DescriptorIndex parameter (TSEG doesn't support range-wise locking). +// +// The LockState and OpenState members that are common to both +// PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL are taken and updated in +// isolation from the rest of the (non-shared) members. +// + +EFI_STATUS +SmramAccessOpen ( + OUT BOOLEAN *LockState, + OUT BOOLEAN *OpenState + ) +{ + // + // Open TSEG by clearing T_EN. + // + PciAnd8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), + (UINT8)((~(UINT32)MCH_ESMRAMC_T_EN) & 0xff)); + + GetStates (LockState, OpenState); + if (!*OpenState) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +EFI_STATUS +SmramAccessClose ( + OUT BOOLEAN *LockState, + OUT BOOLEAN *OpenState + ) +{ + // + // Close TSEG by setting T_EN. + // + PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN); + + GetStates (LockState, OpenState); + if (*OpenState) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +EFI_STATUS +SmramAccessLock ( + OUT BOOLEAN *LockState, + IN OUT BOOLEAN *OpenState + ) +{ + if (*OpenState) { + return EFI_DEVICE_ERROR; + } + + // + // Close & lock TSEG by setting T_EN and D_LCK. + // + PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN); + PciOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM), MCH_SMRAM_D_LCK); + + GetStates (LockState, OpenState); + if (*OpenState || !*LockState) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +EFI_STATUS +SmramAccessGetCapabilities ( + IN BOOLEAN LockState, + IN BOOLEAN OpenState, + IN OUT UINTN *SmramMapSize, + IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap + ) +{ + UINTN OriginalSize; + UINT32 TsegMemoryBaseMb, TsegMemoryBase; + UINT64 CommonRegionState; + UINT8 TsegSizeBits; + + OriginalSize = *SmramMapSize; + *SmramMapSize = DescIdxCount * sizeof *SmramMap; + if (OriginalSize < *SmramMapSize) { + return EFI_BUFFER_TOO_SMALL; + } + + // + // Read the TSEG Memory Base register. + // + TsegMemoryBaseMb = PciRead32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB)); + TsegMemoryBase = (TsegMemoryBaseMb >> MCH_TSEGMB_MB_SHIFT) << 20; + + // + // Precompute the region state bits that will be set for all regions. + // + CommonRegionState = (OpenState ? EFI_SMRAM_OPEN : EFI_SMRAM_CLOSED) | + (LockState ? EFI_SMRAM_LOCKED : 0) | + EFI_CACHEABLE; + + // + // The first region hosts an SMM_S3_RESUME_STATE object. It is located at the + // start of TSEG. We round up the size to whole pages, and we report it as + // EFI_ALLOCATED, so that the SMM_CORE stays away from it. + // + SmramMap[DescIdxSmmS3ResumeState].PhysicalStart = TsegMemoryBase; + SmramMap[DescIdxSmmS3ResumeState].CpuStart = TsegMemoryBase; + SmramMap[DescIdxSmmS3ResumeState].PhysicalSize = + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (sizeof (SMM_S3_RESUME_STATE))); + SmramMap[DescIdxSmmS3ResumeState].RegionState = + CommonRegionState | EFI_ALLOCATED; + + // + // Get the TSEG size bits from the ESMRAMC register. + // + TsegSizeBits = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)) & + MCH_ESMRAMC_TSEG_MASK; + + // + // The second region is the main one, following the first. + // + SmramMap[DescIdxMain].PhysicalStart = + SmramMap[DescIdxSmmS3ResumeState].PhysicalStart + + SmramMap[DescIdxSmmS3ResumeState].PhysicalSize; + SmramMap[DescIdxMain].CpuStart = SmramMap[DescIdxMain].PhysicalStart; + SmramMap[DescIdxMain].PhysicalSize = + (TsegSizeBits == MCH_ESMRAMC_TSEG_8MB ? SIZE_8MB : + TsegSizeBits == MCH_ESMRAMC_TSEG_2MB ? SIZE_2MB : + SIZE_1MB) - SmramMap[DescIdxSmmS3ResumeState].PhysicalSize; + SmramMap[DescIdxMain].RegionState = CommonRegionState; + + return EFI_SUCCESS; +} diff --git a/OvmfPkg/SmmAccess/SmramInternal.h b/OvmfPkg/SmmAccess/SmramInternal.h new file mode 100644 index 0000000000..4e9ac05fad --- /dev/null +++ b/OvmfPkg/SmmAccess/SmramInternal.h @@ -0,0 +1,89 @@ +/** @file + + Functions and types shared by the SMM accessor PEI and DXE modules. + + Copyright (C) 2015, Red Hat, Inc. + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License which 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 + +// +// We'll have two SMRAM ranges. +// +// The first is a tiny one that hosts an SMM_S3_RESUME_STATE object, to be +// filled in by the CPU SMM driver during normal boot, for the PEI instance of +// the LockBox library (which will rely on the object during S3 resume). +// +// The other SMRAM range is the main one, for the SMM core and the SMM drivers. +// +typedef enum { + DescIdxSmmS3ResumeState = 0, + DescIdxMain = 1, + DescIdxCount = 2 +} DESCRIPTOR_INDEX; + +/** + Read the MCH_SMRAM and ESMRAMC registers, and update the LockState and + OpenState fields in the PEI_SMM_ACCESS_PPI / EFI_SMM_ACCESS2_PROTOCOL object, + from the D_LCK and T_EN bits. + + PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions can rely on + the LockState and OpenState fields being up-to-date on entry, and they need + to restore the same invariant on exit, if they touch the bits in question. + + @param[out] LockState Reflects the D_LCK bit on output; TRUE iff SMRAM is + locked. + @param[out] OpenState Reflects the inverse of the T_EN bit on output; TRUE + iff SMRAM is open. +**/ +VOID +GetStates ( + OUT BOOLEAN *LockState, + OUT BOOLEAN *OpenState + ); + +// +// The functions below follow the PEI_SMM_ACCESS_PPI and +// EFI_SMM_ACCESS2_PROTOCOL member declarations. The PeiServices and This +// pointers are removed (TSEG doesn't depend on them), and so is the +// DescriptorIndex parameter (TSEG doesn't support range-wise locking). +// +// The LockState and OpenState members that are common to both +// PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL are taken and updated in +// isolation from the rest of the (non-shared) members. +// + +EFI_STATUS +SmramAccessOpen ( + OUT BOOLEAN *LockState, + OUT BOOLEAN *OpenState + ); + +EFI_STATUS +SmramAccessClose ( + OUT BOOLEAN *LockState, + OUT BOOLEAN *OpenState + ); + +EFI_STATUS +SmramAccessLock ( + OUT BOOLEAN *LockState, + IN OUT BOOLEAN *OpenState + ); + +EFI_STATUS +SmramAccessGetCapabilities ( + IN BOOLEAN LockState, + IN BOOLEAN OpenState, + IN OUT UINTN *SmramMapSize, + IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap + );