]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/SmmAccess/SmmAccessPei.c
OvmfPkg: Apply uncrustify changes
[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 (
181 This->LockState,
182 This->OpenState,
183 SmramMapSize,
184 SmramMap
185 );
186 }
187
188 //
189 // LockState and OpenState will be filled in by the entry point.
190 //
191 STATIC PEI_SMM_ACCESS_PPI mAccess = {
192 &SmmAccessPeiOpen,
193 &SmmAccessPeiClose,
194 &SmmAccessPeiLock,
195 &SmmAccessPeiGetCapabilities
196 };
197
198 STATIC EFI_PEI_PPI_DESCRIPTOR mPpiList[] = {
199 {
200 EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
201 &gPeiSmmAccessPpiGuid, &mAccess
202 }
203 };
204
205 //
206 // Utility functions.
207 //
208 STATIC
209 UINT8
210 CmosRead8 (
211 IN UINT8 Index
212 )
213 {
214 IoWrite8 (0x70, Index);
215 return IoRead8 (0x71);
216 }
217
218 STATIC
219 UINT32
220 GetSystemMemorySizeBelow4gb (
221 VOID
222 )
223 {
224 UINT32 Cmos0x34;
225 UINT32 Cmos0x35;
226
227 Cmos0x34 = CmosRead8 (0x34);
228 Cmos0x35 = CmosRead8 (0x35);
229
230 return ((Cmos0x35 << 8 | Cmos0x34) << 16) + SIZE_16MB;
231 }
232
233 //
234 // Entry point of this driver.
235 //
236 EFI_STATUS
237 EFIAPI
238 SmmAccessPeiEntryPoint (
239 IN EFI_PEI_FILE_HANDLE FileHandle,
240 IN CONST EFI_PEI_SERVICES **PeiServices
241 )
242 {
243 UINT16 HostBridgeDevId;
244 UINT8 EsmramcVal;
245 UINT8 RegMask8;
246 UINT32 TopOfLowRam, TopOfLowRamMb;
247 EFI_STATUS Status;
248 UINTN SmramMapSize;
249 EFI_SMRAM_DESCRIPTOR SmramMap[DescIdxCount];
250 VOID *GuidHob;
251
252 //
253 // This module should only be included if SMRAM support is required.
254 //
255 ASSERT (FeaturePcdGet (PcdSmmSmramRequire));
256
257 //
258 // Verify if we're running on a Q35 machine type.
259 //
260 HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
261 if (HostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {
262 DEBUG ((
263 DEBUG_ERROR,
264 "%a: no SMRAM with host bridge DID=0x%04x; only "
265 "DID=0x%04x (Q35) is supported\n",
266 __FUNCTION__,
267 HostBridgeDevId,
268 INTEL_Q35_MCH_DEVICE_ID
269 ));
270 goto WrongConfig;
271 }
272
273 //
274 // Confirm if QEMU supports SMRAM.
275 //
276 // With no support for it, the ESMRAMC (Extended System Management RAM
277 // Control) register reads as zero. If there is support, the cache-enable
278 // bits are hard-coded as 1 by QEMU.
279 //
280 EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
281 RegMask8 = MCH_ESMRAMC_SM_CACHE | MCH_ESMRAMC_SM_L1 | MCH_ESMRAMC_SM_L2;
282 if ((EsmramcVal & RegMask8) != RegMask8) {
283 DEBUG ((
284 DEBUG_ERROR,
285 "%a: this Q35 implementation lacks SMRAM\n",
286 __FUNCTION__
287 ));
288 goto WrongConfig;
289 }
290
291 TopOfLowRam = GetSystemMemorySizeBelow4gb ();
292 ASSERT ((TopOfLowRam & (SIZE_1MB - 1)) == 0);
293 TopOfLowRamMb = TopOfLowRam >> 20;
294
295 //
296 // Some of the following registers are no-ops for QEMU at the moment, but it
297 // is recommended to set them correctly, since the ESMRAMC that we ultimately
298 // care about is in the same set of registers.
299 //
300 // First, we disable the integrated VGA, and set both the GTT Graphics Memory
301 // Size and the Graphics Mode Select memory pre-allocation fields to zero.
302 // This takes just one write to the Graphics Control Register.
303 //
304 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_GGC), MCH_GGC_IVD);
305
306 //
307 // Set Top of Low Usable DRAM.
308 //
309 PciWrite16 (
310 DRAMC_REGISTER_Q35 (MCH_TOLUD),
311 (UINT16)(TopOfLowRamMb << MCH_TOLUD_MB_SHIFT)
312 );
313
314 //
315 // Given the zero graphics memory sizes configured above, set the
316 // graphics-related stolen memory bases to the same as TOLUD.
317 //
318 PciWrite32 (
319 DRAMC_REGISTER_Q35 (MCH_GBSM),
320 TopOfLowRamMb << MCH_GBSM_MB_SHIFT
321 );
322 PciWrite32 (
323 DRAMC_REGISTER_Q35 (MCH_BGSM),
324 TopOfLowRamMb << MCH_BGSM_MB_SHIFT
325 );
326
327 //
328 // Set TSEG Memory Base.
329 //
330 InitQ35TsegMbytes ();
331 PciWrite32 (
332 DRAMC_REGISTER_Q35 (MCH_TSEGMB),
333 (TopOfLowRamMb - mQ35TsegMbytes) << MCH_TSEGMB_MB_SHIFT
334 );
335
336 //
337 // Set TSEG size, and disable TSEG visibility outside of SMM. Note that the
338 // T_EN bit has inverse meaning; when T_EN is set, then TSEG visibility is
339 // *restricted* to SMM.
340 //
341 EsmramcVal &= ~(UINT32)MCH_ESMRAMC_TSEG_MASK;
342 EsmramcVal |= mQ35TsegMbytes == 8 ? MCH_ESMRAMC_TSEG_8MB :
343 mQ35TsegMbytes == 2 ? MCH_ESMRAMC_TSEG_2MB :
344 mQ35TsegMbytes == 1 ? MCH_ESMRAMC_TSEG_1MB :
345 MCH_ESMRAMC_TSEG_EXT;
346 EsmramcVal |= MCH_ESMRAMC_T_EN;
347 PciWrite8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), EsmramcVal);
348
349 //
350 // TSEG should be closed (see above), but unlocked, initially. Set G_SMRAME
351 // (Global SMRAM Enable) too, as both D_LCK and T_EN depend on it.
352 //
353 PciAndThenOr8 (
354 DRAMC_REGISTER_Q35 (MCH_SMRAM),
355 (UINT8)((~(UINT32)MCH_SMRAM_D_LCK) & 0xff),
356 MCH_SMRAM_G_SMRAME
357 );
358
359 //
360 // Create the GUID HOB and point it to the first SMRAM range.
361 //
362 GetStates (&mAccess.LockState, &mAccess.OpenState);
363 SmramMapSize = sizeof SmramMap;
364 Status = SmramAccessGetCapabilities (
365 mAccess.LockState,
366 mAccess.OpenState,
367 &SmramMapSize,
368 SmramMap
369 );
370 ASSERT_EFI_ERROR (Status);
371
372 DEBUG_CODE_BEGIN ();
373 {
374 UINTN Count;
375 UINTN Idx;
376
377 Count = SmramMapSize / sizeof SmramMap[0];
378 DEBUG ((
379 DEBUG_VERBOSE,
380 "%a: SMRAM map follows, %d entries\n",
381 __FUNCTION__,
382 (INT32)Count
383 ));
384 DEBUG ((
385 DEBUG_VERBOSE,
386 "% 20a % 20a % 20a % 20a\n",
387 "PhysicalStart(0x)",
388 "PhysicalSize(0x)",
389 "CpuStart(0x)",
390 "RegionState(0x)"
391 ));
392 for (Idx = 0; Idx < Count; ++Idx) {
393 DEBUG ((
394 DEBUG_VERBOSE,
395 "% 20Lx % 20Lx % 20Lx % 20Lx\n",
396 SmramMap[Idx].PhysicalStart,
397 SmramMap[Idx].PhysicalSize,
398 SmramMap[Idx].CpuStart,
399 SmramMap[Idx].RegionState
400 ));
401 }
402 }
403 DEBUG_CODE_END ();
404
405 GuidHob = BuildGuidHob (
406 &gEfiAcpiVariableGuid,
407 sizeof SmramMap[DescIdxSmmS3ResumeState]
408 );
409 if (GuidHob == NULL) {
410 return EFI_OUT_OF_RESOURCES;
411 }
412
413 CopyMem (
414 GuidHob,
415 &SmramMap[DescIdxSmmS3ResumeState],
416 sizeof SmramMap[DescIdxSmmS3ResumeState]
417 );
418
419 //
420 // SmramAccessLock() depends on "mQ35SmramAtDefaultSmbase"; init the latter
421 // just before exposing the former via PEI_SMM_ACCESS_PPI.Lock().
422 //
423 InitQ35SmramAtDefaultSmbase ();
424
425 //
426 // We're done. The next step should succeed, but even if it fails, we can't
427 // roll back the above BuildGuidHob() allocation, because PEI doesn't support
428 // releasing memory.
429 //
430 return PeiServicesInstallPpi (mPpiList);
431
432 WrongConfig:
433 //
434 // We really don't want to continue in this case.
435 //
436 ASSERT (FALSE);
437 CpuDeadLoop ();
438 return EFI_UNSUPPORTED;
439 }