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