]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: add PEIM for providing TSEG-as-SMRAM during PEI
authorLaszlo Ersek <lersek@redhat.com>
Mon, 30 Nov 2015 18:41:38 +0000 (18:41 +0000)
committerlersek <lersek@Edk2>
Mon, 30 Nov 2015 18:41:38 +0000 (18:41 +0000)
"MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxPeiLib.inf" is the
LockBoxLib instance with SMRAM access for the PEI phase.

Said library instance must, and can, access the LockBox data in SMRAM
directly if it is invoked before SMBASE relocation / SMI handler
installation. In that case, it only needs PEI_SMM_ACCESS_PPI from the
platform, and it doesn't depend on EFI_PEI_SMM_COMMUNICATION_PPI.

OVMF satisfies the description in SVN r18823 ("MdeModulePkg:
SmmLockBoxPeiLib: work without EFI_PEI_SMM_COMMUNICATION_PPI"): in OVMF,
only S3Resume2Pei links against SmmLockBoxPeiLib.

Therefore, introduce a PEIM that produces the PEI_SMM_ACCESS_PPI
interface, enabling SmmLockBoxPeiLib to work; we can omit including
"UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.inf".

The load / installation order of S3Resume2Pei and SmmAccessPei is
indifferent. SmmAccessPei produces the gEfiAcpiVariableGuid HOB during its
installation (which happens during PEI), but S3Resume2Pei accesses the HOB
only when the DXE IPL calls its S3RestoreConfig2 PPI member, as last act
of PEI.

MCH_SMRAM_D_LCK and MCH_ESMRAMC_T_EN are masked out the way they are, in
SmmAccessPeiEntryPoint() and SmramAccessOpen() respectively, in order to
prevent VS20xx from warning about the (otherwise fully intentional)
truncation in the UINT8 casts. (Warnings reported by Michael Kinney.)

Cc: Michael Kinney <michael.d.kinney@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Michael Kinney <michael.d.kinney@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19040 6f19259b-4bc3-4df7-8a09-765794883524

OvmfPkg/OvmfPkgIa32.dsc
OvmfPkg/OvmfPkgIa32.fdf
OvmfPkg/OvmfPkgIa32X64.dsc
OvmfPkg/OvmfPkgIa32X64.fdf
OvmfPkg/OvmfPkgX64.dsc
OvmfPkg/OvmfPkgX64.fdf
OvmfPkg/SmmAccess/SmmAccessPei.c [new file with mode: 0644]
OvmfPkg/SmmAccess/SmmAccessPei.inf [new file with mode: 0644]
OvmfPkg/SmmAccess/SmramInternal.c [new file with mode: 0644]
OvmfPkg/SmmAccess/SmramInternal.h [new file with mode: 0644]

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