| /** @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 <Guid/AcpiS3Context.h> | |
| #include <IndustryStandard/Q35MchIch9.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/PciLib.h> | |
| #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; | |
| } |