Home | History | Annotate | Download | only in SmmAccess
      1 /** @file
      2 
      3   Functions and types shared by the SMM accessor PEI and DXE modules.
      4 
      5   Copyright (C) 2015, Red Hat, Inc.
      6 
      7   This program and the accompanying materials are licensed and made available
      8   under the terms and conditions of the BSD License which accompanies this
      9   distribution. The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
     13   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include <Guid/AcpiS3Context.h>
     18 #include <IndustryStandard/Q35MchIch9.h>
     19 #include <Library/DebugLib.h>
     20 #include <Library/PciLib.h>
     21 
     22 #include "SmramInternal.h"
     23 
     24 /**
     25   Read the MCH_SMRAM and ESMRAMC registers, and update the LockState and
     26   OpenState fields in the PEI_SMM_ACCESS_PPI / EFI_SMM_ACCESS2_PROTOCOL object,
     27   from the D_LCK and T_EN bits.
     28 
     29   PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions can rely on
     30   the LockState and OpenState fields being up-to-date on entry, and they need
     31   to restore the same invariant on exit, if they touch the bits in question.
     32 
     33   @param[out] LockState  Reflects the D_LCK bit on output; TRUE iff SMRAM is
     34                          locked.
     35   @param[out] OpenState  Reflects the inverse of the T_EN bit on output; TRUE
     36                          iff SMRAM is open.
     37 **/
     38 VOID
     39 GetStates (
     40   OUT BOOLEAN *LockState,
     41   OUT BOOLEAN *OpenState
     42 )
     43 {
     44   UINT8 SmramVal, EsmramcVal;
     45 
     46   SmramVal   = PciRead8 (DRAMC_REGISTER_Q35 (MCH_SMRAM));
     47   EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
     48 
     49   *LockState = !!(SmramVal & MCH_SMRAM_D_LCK);
     50   *OpenState = !(EsmramcVal & MCH_ESMRAMC_T_EN);
     51 }
     52 
     53 //
     54 // The functions below follow the PEI_SMM_ACCESS_PPI and
     55 // EFI_SMM_ACCESS2_PROTOCOL member declarations. The PeiServices and This
     56 // pointers are removed (TSEG doesn't depend on them), and so is the
     57 // DescriptorIndex parameter (TSEG doesn't support range-wise locking).
     58 //
     59 // The LockState and OpenState members that are common to both
     60 // PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL are taken and updated in
     61 // isolation from the rest of the (non-shared) members.
     62 //
     63 
     64 EFI_STATUS
     65 SmramAccessOpen (
     66   OUT BOOLEAN *LockState,
     67   OUT BOOLEAN *OpenState
     68   )
     69 {
     70   //
     71   // Open TSEG by clearing T_EN.
     72   //
     73   PciAnd8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC),
     74     (UINT8)((~(UINT32)MCH_ESMRAMC_T_EN) & 0xff));
     75 
     76   GetStates (LockState, OpenState);
     77   if (!*OpenState) {
     78     return EFI_DEVICE_ERROR;
     79   }
     80   return EFI_SUCCESS;
     81 }
     82 
     83 EFI_STATUS
     84 SmramAccessClose (
     85   OUT BOOLEAN *LockState,
     86   OUT BOOLEAN *OpenState
     87   )
     88 {
     89   //
     90   // Close TSEG by setting T_EN.
     91   //
     92   PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
     93 
     94   GetStates (LockState, OpenState);
     95   if (*OpenState) {
     96     return EFI_DEVICE_ERROR;
     97   }
     98   return EFI_SUCCESS;
     99 }
    100 
    101 EFI_STATUS
    102 SmramAccessLock (
    103   OUT    BOOLEAN *LockState,
    104   IN OUT BOOLEAN *OpenState
    105   )
    106 {
    107   if (*OpenState) {
    108     return EFI_DEVICE_ERROR;
    109   }
    110 
    111   //
    112   // Close & lock TSEG by setting T_EN and D_LCK.
    113   //
    114   PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
    115   PciOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM),   MCH_SMRAM_D_LCK);
    116 
    117   GetStates (LockState, OpenState);
    118   if (*OpenState || !*LockState) {
    119     return EFI_DEVICE_ERROR;
    120   }
    121   return EFI_SUCCESS;
    122 }
    123 
    124 EFI_STATUS
    125 SmramAccessGetCapabilities (
    126   IN BOOLEAN                  LockState,
    127   IN BOOLEAN                  OpenState,
    128   IN OUT UINTN                *SmramMapSize,
    129   IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap
    130   )
    131 {
    132   UINTN  OriginalSize;
    133   UINT32 TsegMemoryBaseMb, TsegMemoryBase;
    134   UINT64 CommonRegionState;
    135   UINT8  TsegSizeBits;
    136 
    137   OriginalSize  = *SmramMapSize;
    138   *SmramMapSize = DescIdxCount * sizeof *SmramMap;
    139   if (OriginalSize < *SmramMapSize) {
    140     return EFI_BUFFER_TOO_SMALL;
    141   }
    142 
    143   //
    144   // Read the TSEG Memory Base register.
    145   //
    146   TsegMemoryBaseMb = PciRead32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB));
    147   TsegMemoryBase = (TsegMemoryBaseMb >> MCH_TSEGMB_MB_SHIFT) << 20;
    148 
    149   //
    150   // Precompute the region state bits that will be set for all regions.
    151   //
    152   CommonRegionState = (OpenState ? EFI_SMRAM_OPEN : EFI_SMRAM_CLOSED) |
    153                       (LockState ? EFI_SMRAM_LOCKED : 0) |
    154                       EFI_CACHEABLE;
    155 
    156   //
    157   // The first region hosts an SMM_S3_RESUME_STATE object. It is located at the
    158   // start of TSEG. We round up the size to whole pages, and we report it as
    159   // EFI_ALLOCATED, so that the SMM_CORE stays away from it.
    160   //
    161   SmramMap[DescIdxSmmS3ResumeState].PhysicalStart = TsegMemoryBase;
    162   SmramMap[DescIdxSmmS3ResumeState].CpuStart      = TsegMemoryBase;
    163   SmramMap[DescIdxSmmS3ResumeState].PhysicalSize  =
    164     EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (sizeof (SMM_S3_RESUME_STATE)));
    165   SmramMap[DescIdxSmmS3ResumeState].RegionState   =
    166     CommonRegionState | EFI_ALLOCATED;
    167 
    168   //
    169   // Get the TSEG size bits from the ESMRAMC register.
    170   //
    171   TsegSizeBits = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)) &
    172                  MCH_ESMRAMC_TSEG_MASK;
    173 
    174   //
    175   // The second region is the main one, following the first.
    176   //
    177   SmramMap[DescIdxMain].PhysicalStart =
    178     SmramMap[DescIdxSmmS3ResumeState].PhysicalStart +
    179     SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
    180   SmramMap[DescIdxMain].CpuStart = SmramMap[DescIdxMain].PhysicalStart;
    181   SmramMap[DescIdxMain].PhysicalSize =
    182     (TsegSizeBits == MCH_ESMRAMC_TSEG_8MB ? SIZE_8MB :
    183      TsegSizeBits == MCH_ESMRAMC_TSEG_2MB ? SIZE_2MB :
    184      SIZE_1MB) - SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
    185   SmramMap[DescIdxMain].RegionState = CommonRegionState;
    186 
    187   return EFI_SUCCESS;
    188 }
    189