summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/SmmAccess/SmmAccessPei.c
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/SmmAccess/SmmAccessPei.c')
-rw-r--r--OvmfPkg/SmmAccess/SmmAccessPei.c393
1 files changed, 393 insertions, 0 deletions
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.<BR>
+ Copyright (c) 2010, 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 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 <Guid/AcpiS3Context.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Ppi/SmmAccess.h>
+
+#include <OvmfPlatforms.h>
+
+#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;
+}