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