]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/SmmAccess/SmramInternal.c
OvmfPkg/Csm/LegacyBiosDxe: Fix Legacy16GetTableAddress call for E820 data
[mirror_edk2.git] / OvmfPkg / SmmAccess / SmramInternal.c
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 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <Guid/AcpiS3Context.h>
12 #include <IndustryStandard/Q35MchIch9.h>
13 #include <Library/DebugLib.h>
14 #include <Library/PcdLib.h>
15 #include <Library/PciLib.h>
16
17 #include "SmramInternal.h"
18
19 //
20 // The value of PcdQ35TsegMbytes is saved into this variable at module startup.
21 //
22 UINT16 mQ35TsegMbytes;
23
24 /**
25 Save PcdQ35TsegMbytes into mQ35TsegMbytes.
26 **/
27 VOID
28 InitQ35TsegMbytes (
29 VOID
30 )
31 {
32 mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
33 }
34
35 /**
36 Read the MCH_SMRAM and ESMRAMC registers, and update the LockState and
37 OpenState fields in the PEI_SMM_ACCESS_PPI / EFI_SMM_ACCESS2_PROTOCOL object,
38 from the D_LCK and T_EN bits.
39
40 PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions can rely on
41 the LockState and OpenState fields being up-to-date on entry, and they need
42 to restore the same invariant on exit, if they touch the bits in question.
43
44 @param[out] LockState Reflects the D_LCK bit on output; TRUE iff SMRAM is
45 locked.
46 @param[out] OpenState Reflects the inverse of the T_EN bit on output; TRUE
47 iff SMRAM is open.
48 **/
49 VOID
50 GetStates (
51 OUT BOOLEAN *LockState,
52 OUT BOOLEAN *OpenState
53 )
54 {
55 UINT8 SmramVal, EsmramcVal;
56
57 SmramVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_SMRAM));
58 EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
59
60 *LockState = !!(SmramVal & MCH_SMRAM_D_LCK);
61 *OpenState = !(EsmramcVal & MCH_ESMRAMC_T_EN);
62 }
63
64 //
65 // The functions below follow the PEI_SMM_ACCESS_PPI and
66 // EFI_SMM_ACCESS2_PROTOCOL member declarations. The PeiServices and This
67 // pointers are removed (TSEG doesn't depend on them), and so is the
68 // DescriptorIndex parameter (TSEG doesn't support range-wise locking).
69 //
70 // The LockState and OpenState members that are common to both
71 // PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL are taken and updated in
72 // isolation from the rest of the (non-shared) members.
73 //
74
75 EFI_STATUS
76 SmramAccessOpen (
77 OUT BOOLEAN *LockState,
78 OUT BOOLEAN *OpenState
79 )
80 {
81 //
82 // Open TSEG by clearing T_EN.
83 //
84 PciAnd8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC),
85 (UINT8)((~(UINT32)MCH_ESMRAMC_T_EN) & 0xff));
86
87 GetStates (LockState, OpenState);
88 if (!*OpenState) {
89 return EFI_DEVICE_ERROR;
90 }
91 return EFI_SUCCESS;
92 }
93
94 EFI_STATUS
95 SmramAccessClose (
96 OUT BOOLEAN *LockState,
97 OUT BOOLEAN *OpenState
98 )
99 {
100 //
101 // Close TSEG by setting T_EN.
102 //
103 PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
104
105 GetStates (LockState, OpenState);
106 if (*OpenState) {
107 return EFI_DEVICE_ERROR;
108 }
109 return EFI_SUCCESS;
110 }
111
112 EFI_STATUS
113 SmramAccessLock (
114 OUT BOOLEAN *LockState,
115 IN OUT BOOLEAN *OpenState
116 )
117 {
118 if (*OpenState) {
119 return EFI_DEVICE_ERROR;
120 }
121
122 //
123 // Close & lock TSEG by setting T_EN and D_LCK.
124 //
125 PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
126 PciOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM), MCH_SMRAM_D_LCK);
127
128 GetStates (LockState, OpenState);
129 if (*OpenState || !*LockState) {
130 return EFI_DEVICE_ERROR;
131 }
132 return EFI_SUCCESS;
133 }
134
135 EFI_STATUS
136 SmramAccessGetCapabilities (
137 IN BOOLEAN LockState,
138 IN BOOLEAN OpenState,
139 IN OUT UINTN *SmramMapSize,
140 IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap
141 )
142 {
143 UINTN OriginalSize;
144 UINT32 TsegMemoryBaseMb, TsegMemoryBase;
145 UINT64 CommonRegionState;
146 UINT8 TsegSizeBits;
147
148 OriginalSize = *SmramMapSize;
149 *SmramMapSize = DescIdxCount * sizeof *SmramMap;
150 if (OriginalSize < *SmramMapSize) {
151 return EFI_BUFFER_TOO_SMALL;
152 }
153
154 //
155 // Read the TSEG Memory Base register.
156 //
157 TsegMemoryBaseMb = PciRead32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB));
158 TsegMemoryBase = (TsegMemoryBaseMb >> MCH_TSEGMB_MB_SHIFT) << 20;
159
160 //
161 // Precompute the region state bits that will be set for all regions.
162 //
163 CommonRegionState = (OpenState ? EFI_SMRAM_OPEN : EFI_SMRAM_CLOSED) |
164 (LockState ? EFI_SMRAM_LOCKED : 0) |
165 EFI_CACHEABLE;
166
167 //
168 // The first region hosts an SMM_S3_RESUME_STATE object. It is located at the
169 // start of TSEG. We round up the size to whole pages, and we report it as
170 // EFI_ALLOCATED, so that the SMM_CORE stays away from it.
171 //
172 SmramMap[DescIdxSmmS3ResumeState].PhysicalStart = TsegMemoryBase;
173 SmramMap[DescIdxSmmS3ResumeState].CpuStart = TsegMemoryBase;
174 SmramMap[DescIdxSmmS3ResumeState].PhysicalSize =
175 EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (sizeof (SMM_S3_RESUME_STATE)));
176 SmramMap[DescIdxSmmS3ResumeState].RegionState =
177 CommonRegionState | EFI_ALLOCATED;
178
179 //
180 // Get the TSEG size bits from the ESMRAMC register.
181 //
182 TsegSizeBits = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)) &
183 MCH_ESMRAMC_TSEG_MASK;
184
185 //
186 // The second region is the main one, following the first.
187 //
188 SmramMap[DescIdxMain].PhysicalStart =
189 SmramMap[DescIdxSmmS3ResumeState].PhysicalStart +
190 SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
191 SmramMap[DescIdxMain].CpuStart = SmramMap[DescIdxMain].PhysicalStart;
192 SmramMap[DescIdxMain].PhysicalSize =
193 (TsegSizeBits == MCH_ESMRAMC_TSEG_8MB ? SIZE_8MB :
194 TsegSizeBits == MCH_ESMRAMC_TSEG_2MB ? SIZE_2MB :
195 TsegSizeBits == MCH_ESMRAMC_TSEG_1MB ? SIZE_1MB :
196 mQ35TsegMbytes * SIZE_1MB) -
197 SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
198 SmramMap[DescIdxMain].RegionState = CommonRegionState;
199
200 return EFI_SUCCESS;
201 }