Home | History | Annotate | Download | only in SmmAccess
      1 /** @file
      2 
      3   A PEIM with the following responsibilities:
      4 
      5   - verify & configure the Q35 TSEG in the entry point,
      6   - provide SMRAM access by producing PEI_SMM_ACCESS_PPI,
      7   - set aside the SMM_S3_RESUME_STATE object at the bottom of TSEG, and expose
      8     it via the gEfiAcpiVariableGuid GUID HOB.
      9 
     10   This PEIM runs from RAM, so we can write to variables with static storage
     11   duration.
     12 
     13   Copyright (C) 2013, 2015, Red Hat, Inc.<BR>
     14   Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
     15 
     16   This program and the accompanying materials are licensed and made available
     17   under the terms and conditions of the BSD License which accompanies this
     18   distribution. The full text of the license may be found at
     19   http://opensource.org/licenses/bsd-license.php
     20 
     21   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
     22   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     23 
     24 **/
     25 
     26 #include <Guid/AcpiS3Context.h>
     27 #include <Library/BaseLib.h>
     28 #include <Library/BaseMemoryLib.h>
     29 #include <Library/DebugLib.h>
     30 #include <Library/HobLib.h>
     31 #include <Library/IoLib.h>
     32 #include <Library/PcdLib.h>
     33 #include <Library/PciLib.h>
     34 #include <Library/PeiServicesLib.h>
     35 #include <Ppi/SmmAccess.h>
     36 
     37 #include <OvmfPlatforms.h>
     38 
     39 #include "SmramInternal.h"
     40 
     41 //
     42 // PEI_SMM_ACCESS_PPI implementation.
     43 //
     44 
     45 /**
     46   Opens the SMRAM area to be accessible by a PEIM driver.
     47 
     48   This function "opens" SMRAM so that it is visible while not inside of SMM.
     49   The function should return EFI_UNSUPPORTED if the hardware does not support
     50   hiding of SMRAM. The function should return EFI_DEVICE_ERROR if the SMRAM
     51   configuration is locked.
     52 
     53   @param  PeiServices            General purpose services available to every
     54                                  PEIM.
     55   @param  This                   The pointer to the SMM Access Interface.
     56   @param  DescriptorIndex        The region of SMRAM to Open.
     57 
     58   @retval EFI_SUCCESS            The region was successfully opened.
     59   @retval EFI_DEVICE_ERROR       The region could not be opened because locked
     60                                  by chipset.
     61   @retval EFI_INVALID_PARAMETER  The descriptor index was out of bounds.
     62 
     63 **/
     64 STATIC
     65 EFI_STATUS
     66 EFIAPI
     67 SmmAccessPeiOpen (
     68   IN EFI_PEI_SERVICES                **PeiServices,
     69   IN PEI_SMM_ACCESS_PPI              *This,
     70   IN UINTN                           DescriptorIndex
     71   )
     72 {
     73   if (DescriptorIndex >= DescIdxCount) {
     74     return EFI_INVALID_PARAMETER;
     75   }
     76 
     77   //
     78   // According to current practice, DescriptorIndex is not considered at all,
     79   // beyond validating it.
     80   //
     81   return SmramAccessOpen (&This->LockState, &This->OpenState);
     82 }
     83 
     84 /**
     85   Inhibits access to the SMRAM.
     86 
     87   This function "closes" SMRAM so that it is not visible while outside of SMM.
     88   The function should return EFI_UNSUPPORTED if the hardware does not support
     89   hiding of SMRAM.
     90 
     91   @param  PeiServices              General purpose services available to every
     92                                    PEIM.
     93   @param  This                     The pointer to the SMM Access Interface.
     94   @param  DescriptorIndex          The region of SMRAM to Close.
     95 
     96   @retval EFI_SUCCESS              The region was successfully closed.
     97   @retval EFI_DEVICE_ERROR         The region could not be closed because
     98                                    locked by chipset.
     99   @retval EFI_INVALID_PARAMETER    The descriptor index was out of bounds.
    100 
    101 **/
    102 STATIC
    103 EFI_STATUS
    104 EFIAPI
    105 SmmAccessPeiClose (
    106   IN EFI_PEI_SERVICES                **PeiServices,
    107   IN PEI_SMM_ACCESS_PPI              *This,
    108   IN UINTN                           DescriptorIndex
    109   )
    110 {
    111   if (DescriptorIndex >= DescIdxCount) {
    112     return EFI_INVALID_PARAMETER;
    113   }
    114 
    115   //
    116   // According to current practice, DescriptorIndex is not considered at all,
    117   // beyond validating it.
    118   //
    119   return SmramAccessClose (&This->LockState, &This->OpenState);
    120 }
    121 
    122 /**
    123   Inhibits access to the SMRAM.
    124 
    125   This function prohibits access to the SMRAM region.  This function is usually
    126   implemented such that it is a write-once operation.
    127 
    128   @param  PeiServices              General purpose services available to every
    129                                    PEIM.
    130   @param  This                     The pointer to the SMM Access Interface.
    131   @param  DescriptorIndex          The region of SMRAM to Close.
    132 
    133   @retval EFI_SUCCESS            The region was successfully locked.
    134   @retval EFI_DEVICE_ERROR       The region could not be locked because at
    135                                  least one range is still open.
    136   @retval EFI_INVALID_PARAMETER  The descriptor index was out of bounds.
    137 
    138 **/
    139 STATIC
    140 EFI_STATUS
    141 EFIAPI
    142 SmmAccessPeiLock (
    143   IN EFI_PEI_SERVICES                **PeiServices,
    144   IN PEI_SMM_ACCESS_PPI              *This,
    145   IN UINTN                           DescriptorIndex
    146   )
    147 {
    148   if (DescriptorIndex >= DescIdxCount) {
    149     return EFI_INVALID_PARAMETER;
    150   }
    151 
    152   //
    153   // According to current practice, DescriptorIndex is not considered at all,
    154   // beyond validating it.
    155   //
    156   return SmramAccessLock (&This->LockState, &This->OpenState);
    157 }
    158 
    159 /**
    160   Queries the memory controller for the possible regions that will support
    161   SMRAM.
    162 
    163   @param  PeiServices           General purpose services available to every
    164                                 PEIM.
    165   @param This                   The pointer to the SmmAccessPpi Interface.
    166   @param SmramMapSize           The pointer to the variable containing size of
    167                                 the buffer to contain the description
    168                                 information.
    169   @param SmramMap               The buffer containing the data describing the
    170                                 Smram region descriptors.
    171 
    172   @retval EFI_BUFFER_TOO_SMALL  The user did not provide a sufficient buffer.
    173   @retval EFI_SUCCESS           The user provided a sufficiently-sized buffer.
    174 
    175 **/
    176 STATIC
    177 EFI_STATUS
    178 EFIAPI
    179 SmmAccessPeiGetCapabilities (
    180   IN EFI_PEI_SERVICES                **PeiServices,
    181   IN PEI_SMM_ACCESS_PPI              *This,
    182   IN OUT UINTN                       *SmramMapSize,
    183   IN OUT EFI_SMRAM_DESCRIPTOR        *SmramMap
    184   )
    185 {
    186   return SmramAccessGetCapabilities (This->LockState, This->OpenState,
    187            SmramMapSize, SmramMap);
    188 }
    189 
    190 //
    191 // LockState and OpenState will be filled in by the entry point.
    192 //
    193 STATIC PEI_SMM_ACCESS_PPI mAccess = {
    194   &SmmAccessPeiOpen,
    195   &SmmAccessPeiClose,
    196   &SmmAccessPeiLock,
    197   &SmmAccessPeiGetCapabilities
    198 };
    199 
    200 
    201 STATIC EFI_PEI_PPI_DESCRIPTOR mPpiList[] = {
    202   {
    203     EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
    204     &gPeiSmmAccessPpiGuid, &mAccess
    205   }
    206 };
    207 
    208 
    209 //
    210 // Utility functions.
    211 //
    212 STATIC
    213 UINT8
    214 CmosRead8 (
    215   IN UINT8 Index
    216   )
    217 {
    218   IoWrite8 (0x70, Index);
    219   return IoRead8 (0x71);
    220 }
    221 
    222 STATIC
    223 UINT32
    224 GetSystemMemorySizeBelow4gb (
    225   VOID
    226   )
    227 {
    228   UINT32 Cmos0x34;
    229   UINT32 Cmos0x35;
    230 
    231   Cmos0x34 = CmosRead8 (0x34);
    232   Cmos0x35 = CmosRead8 (0x35);
    233 
    234   return ((Cmos0x35 << 8 | Cmos0x34) << 16) + SIZE_16MB;
    235 }
    236 
    237 
    238 //
    239 // Entry point of this driver.
    240 //
    241 EFI_STATUS
    242 EFIAPI
    243 SmmAccessPeiEntryPoint (
    244   IN       EFI_PEI_FILE_HANDLE  FileHandle,
    245   IN CONST EFI_PEI_SERVICES     **PeiServices
    246   )
    247 {
    248   UINT16               HostBridgeDevId;
    249   UINT8                EsmramcVal;
    250   UINT8                RegMask8;
    251   UINT32               TopOfLowRam, TopOfLowRamMb;
    252   EFI_STATUS           Status;
    253   UINTN                SmramMapSize;
    254   EFI_SMRAM_DESCRIPTOR SmramMap[DescIdxCount];
    255   VOID                 *GuidHob;
    256 
    257   //
    258   // This module should only be included if SMRAM support is required.
    259   //
    260   ASSERT (FeaturePcdGet (PcdSmmSmramRequire));
    261 
    262   //
    263   // Verify if we're running on a Q35 machine type.
    264   //
    265   HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
    266   if (HostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {
    267     DEBUG ((EFI_D_ERROR, "%a: no SMRAM with host bridge DID=0x%04x; only "
    268       "DID=0x%04x (Q35) is supported\n", __FUNCTION__, HostBridgeDevId,
    269       INTEL_Q35_MCH_DEVICE_ID));
    270     goto WrongConfig;
    271   }
    272 
    273   //
    274   // Confirm if QEMU supports SMRAM.
    275   //
    276   // With no support for it, the ESMRAMC (Extended System Management RAM
    277   // Control) register reads as zero. If there is support, the cache-enable
    278   // bits are hard-coded as 1 by QEMU.
    279   //
    280   EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
    281   RegMask8 = MCH_ESMRAMC_SM_CACHE | MCH_ESMRAMC_SM_L1 | MCH_ESMRAMC_SM_L2;
    282   if ((EsmramcVal & RegMask8) != RegMask8) {
    283     DEBUG ((EFI_D_ERROR, "%a: this Q35 implementation lacks SMRAM\n",
    284       __FUNCTION__));
    285     goto WrongConfig;
    286   }
    287 
    288   TopOfLowRam = GetSystemMemorySizeBelow4gb ();
    289   ASSERT ((TopOfLowRam & (SIZE_1MB - 1)) == 0);
    290   TopOfLowRamMb = TopOfLowRam >> 20;
    291 
    292   //
    293   // Some of the following registers are no-ops for QEMU at the moment, but it
    294   // is recommended to set them correctly, since the ESMRAMC that we ultimately
    295   // care about is in the same set of registers.
    296   //
    297   // First, we disable the integrated VGA, and set both the GTT Graphics Memory
    298   // Size and the Graphics Mode Select memory pre-allocation fields to zero.
    299   // This takes just one write to the Graphics Control Register.
    300   //
    301   PciWrite16 (DRAMC_REGISTER_Q35 (MCH_GGC), MCH_GGC_IVD);
    302 
    303   //
    304   // Set Top of Low Usable DRAM.
    305   //
    306   PciWrite16 (DRAMC_REGISTER_Q35 (MCH_TOLUD),
    307     (UINT16)(TopOfLowRamMb << MCH_TOLUD_MB_SHIFT));
    308 
    309   //
    310   // Given the zero graphics memory sizes configured above, set the
    311   // graphics-related stolen memory bases to the same as TOLUD.
    312   //
    313   PciWrite32 (DRAMC_REGISTER_Q35 (MCH_GBSM),
    314     TopOfLowRamMb << MCH_GBSM_MB_SHIFT);
    315   PciWrite32 (DRAMC_REGISTER_Q35 (MCH_BGSM),
    316     TopOfLowRamMb << MCH_BGSM_MB_SHIFT);
    317 
    318   //
    319   // Set TSEG Memory Base.
    320   //
    321   PciWrite32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB),
    322     (TopOfLowRamMb - FixedPcdGet8 (PcdQ35TsegMbytes)) << MCH_TSEGMB_MB_SHIFT);
    323 
    324   //
    325   // Set TSEG size, and disable TSEG visibility outside of SMM. Note that the
    326   // T_EN bit has inverse meaning; when T_EN is set, then TSEG visibility is
    327   // *restricted* to SMM.
    328   //
    329   EsmramcVal &= ~(UINT32)MCH_ESMRAMC_TSEG_MASK;
    330   EsmramcVal |= FixedPcdGet8 (PcdQ35TsegMbytes) == 8 ? MCH_ESMRAMC_TSEG_8MB :
    331                 FixedPcdGet8 (PcdQ35TsegMbytes) == 2 ? MCH_ESMRAMC_TSEG_2MB :
    332                 MCH_ESMRAMC_TSEG_1MB;
    333   EsmramcVal |= MCH_ESMRAMC_T_EN;
    334   PciWrite8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), EsmramcVal);
    335 
    336   //
    337   // TSEG should be closed (see above), but unlocked, initially. Set G_SMRAME
    338   // (Global SMRAM Enable) too, as both D_LCK and T_EN depend on it.
    339   //
    340   PciAndThenOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM),
    341     (UINT8)((~(UINT32)MCH_SMRAM_D_LCK) & 0xff), MCH_SMRAM_G_SMRAME);
    342 
    343   //
    344   // Create the GUID HOB and point it to the first SMRAM range.
    345   //
    346   GetStates (&mAccess.LockState, &mAccess.OpenState);
    347   SmramMapSize = sizeof SmramMap;
    348   Status = SmramAccessGetCapabilities (mAccess.LockState, mAccess.OpenState,
    349              &SmramMapSize, SmramMap);
    350   ASSERT_EFI_ERROR (Status);
    351 
    352   DEBUG_CODE_BEGIN ();
    353   {
    354     UINTN Count;
    355     UINTN Idx;
    356 
    357     Count = SmramMapSize / sizeof SmramMap[0];
    358     DEBUG ((EFI_D_VERBOSE, "%a: SMRAM map follows, %d entries\n", __FUNCTION__,
    359       (INT32)Count));
    360     DEBUG ((EFI_D_VERBOSE, "% 20a % 20a % 20a % 20a\n", "PhysicalStart(0x)",
    361       "PhysicalSize(0x)", "CpuStart(0x)", "RegionState(0x)"));
    362     for (Idx = 0; Idx < Count; ++Idx) {
    363       DEBUG ((EFI_D_VERBOSE, "% 20Lx % 20Lx % 20Lx % 20Lx\n",
    364         SmramMap[Idx].PhysicalStart, SmramMap[Idx].PhysicalSize,
    365         SmramMap[Idx].CpuStart, SmramMap[Idx].RegionState));
    366     }
    367   }
    368   DEBUG_CODE_END ();
    369 
    370   GuidHob = BuildGuidHob (&gEfiAcpiVariableGuid,
    371     sizeof SmramMap[DescIdxSmmS3ResumeState]);
    372   if (GuidHob == NULL) {
    373     return EFI_OUT_OF_RESOURCES;
    374   }
    375 
    376   CopyMem (GuidHob, &SmramMap[DescIdxSmmS3ResumeState],
    377     sizeof SmramMap[DescIdxSmmS3ResumeState]);
    378 
    379   //
    380   // We're done. The next step should succeed, but even if it fails, we can't
    381   // roll back the above BuildGuidHob() allocation, because PEI doesn't support
    382   // releasing memory.
    383   //
    384   return PeiServicesInstallPpi (mPpiList);
    385 
    386 WrongConfig:
    387   //
    388   // We really don't want to continue in this case.
    389   //
    390   ASSERT (FALSE);
    391   CpuDeadLoop ();
    392   return EFI_UNSUPPORTED;
    393 }
    394