3 A PEIM with the following responsibilities:
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.
10 This PEIM runs from RAM, so we can write to variables with static storage
13 Copyright (C) 2013, 2015, Red Hat, Inc.<BR>
14 Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
16 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
31 #include <OvmfPlatforms.h>
33 #include "SmramInternal.h"
36 // PEI_SMM_ACCESS_PPI implementation.
40 Opens the SMRAM area to be accessible by a PEIM driver.
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.
47 @param PeiServices General purpose services available to every
49 @param This The pointer to the SMM Access Interface.
50 @param DescriptorIndex The region of SMRAM to Open.
52 @retval EFI_SUCCESS The region was successfully opened.
53 @retval EFI_DEVICE_ERROR The region could not be opened because locked
55 @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds.
62 IN EFI_PEI_SERVICES
**PeiServices
,
63 IN PEI_SMM_ACCESS_PPI
*This
,
64 IN UINTN DescriptorIndex
67 if (DescriptorIndex
>= DescIdxCount
) {
68 return EFI_INVALID_PARAMETER
;
72 // According to current practice, DescriptorIndex is not considered at all,
73 // beyond validating it.
75 return SmramAccessOpen (&This
->LockState
, &This
->OpenState
);
79 Inhibits access to the SMRAM.
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
85 @param PeiServices General purpose services available to every
87 @param This The pointer to the SMM Access Interface.
88 @param DescriptorIndex The region of SMRAM to Close.
90 @retval EFI_SUCCESS The region was successfully closed.
91 @retval EFI_DEVICE_ERROR The region could not be closed because
93 @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds.
100 IN EFI_PEI_SERVICES
**PeiServices
,
101 IN PEI_SMM_ACCESS_PPI
*This
,
102 IN UINTN DescriptorIndex
105 if (DescriptorIndex
>= DescIdxCount
) {
106 return EFI_INVALID_PARAMETER
;
110 // According to current practice, DescriptorIndex is not considered at all,
111 // beyond validating it.
113 return SmramAccessClose (&This
->LockState
, &This
->OpenState
);
117 Inhibits access to the SMRAM.
119 This function prohibits access to the SMRAM region. This function is usually
120 implemented such that it is a write-once operation.
122 @param PeiServices General purpose services available to every
124 @param This The pointer to the SMM Access Interface.
125 @param DescriptorIndex The region of SMRAM to Close.
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.
137 IN EFI_PEI_SERVICES
**PeiServices
,
138 IN PEI_SMM_ACCESS_PPI
*This
,
139 IN UINTN DescriptorIndex
142 if (DescriptorIndex
>= DescIdxCount
) {
143 return EFI_INVALID_PARAMETER
;
147 // According to current practice, DescriptorIndex is not considered at all,
148 // beyond validating it.
150 return SmramAccessLock (&This
->LockState
, &This
->OpenState
);
154 Queries the memory controller for the possible regions that will support
157 @param PeiServices General purpose services available to every
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
163 @param SmramMap The buffer containing the data describing the
164 Smram region descriptors.
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.
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
180 return SmramAccessGetCapabilities (This
->LockState
, This
->OpenState
,
181 SmramMapSize
, SmramMap
);
185 // LockState and OpenState will be filled in by the entry point.
187 STATIC PEI_SMM_ACCESS_PPI mAccess
= {
191 &SmmAccessPeiGetCapabilities
195 STATIC EFI_PEI_PPI_DESCRIPTOR mPpiList
[] = {
197 EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
,
198 &gPeiSmmAccessPpiGuid
, &mAccess
204 // Utility functions.
212 IoWrite8 (0x70, Index
);
213 return IoRead8 (0x71);
218 GetSystemMemorySizeBelow4gb (
225 Cmos0x34
= CmosRead8 (0x34);
226 Cmos0x35
= CmosRead8 (0x35);
228 return ((Cmos0x35
<< 8 | Cmos0x34
) << 16) + SIZE_16MB
;
233 // Entry point of this driver.
237 SmmAccessPeiEntryPoint (
238 IN EFI_PEI_FILE_HANDLE FileHandle
,
239 IN CONST EFI_PEI_SERVICES
**PeiServices
242 UINT16 HostBridgeDevId
;
245 UINT32 TopOfLowRam
, TopOfLowRamMb
;
248 EFI_SMRAM_DESCRIPTOR SmramMap
[DescIdxCount
];
252 // This module should only be included if SMRAM support is required.
254 ASSERT (FeaturePcdGet (PcdSmmSmramRequire
));
257 // Verify if we're running on a Q35 machine type.
259 HostBridgeDevId
= PciRead16 (OVMF_HOSTBRIDGE_DID
);
260 if (HostBridgeDevId
!= INTEL_Q35_MCH_DEVICE_ID
) {
261 DEBUG ((DEBUG_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
));
268 // Confirm if QEMU supports SMRAM.
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.
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 ((DEBUG_ERROR
, "%a: this Q35 implementation lacks SMRAM\n",
282 TopOfLowRam
= GetSystemMemorySizeBelow4gb ();
283 ASSERT ((TopOfLowRam
& (SIZE_1MB
- 1)) == 0);
284 TopOfLowRamMb
= TopOfLowRam
>> 20;
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.
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.
295 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_GGC
), MCH_GGC_IVD
);
298 // Set Top of Low Usable DRAM.
300 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_TOLUD
),
301 (UINT16
)(TopOfLowRamMb
<< MCH_TOLUD_MB_SHIFT
));
304 // Given the zero graphics memory sizes configured above, set the
305 // graphics-related stolen memory bases to the same as TOLUD.
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
);
313 // Set TSEG Memory Base.
315 InitQ35TsegMbytes ();
316 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB
),
317 (TopOfLowRamMb
- mQ35TsegMbytes
) << MCH_TSEGMB_MB_SHIFT
);
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.
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
);
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.
336 PciAndThenOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM
),
337 (UINT8
)((~(UINT32
)MCH_SMRAM_D_LCK
) & 0xff), MCH_SMRAM_G_SMRAME
);
340 // Create the GUID HOB and point it to the first SMRAM range.
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
);
353 Count
= SmramMapSize
/ sizeof SmramMap
[0];
354 DEBUG ((DEBUG_VERBOSE
, "%a: SMRAM map follows, %d entries\n", __FUNCTION__
,
356 DEBUG ((DEBUG_VERBOSE
, "% 20a % 20a % 20a % 20a\n", "PhysicalStart(0x)",
357 "PhysicalSize(0x)", "CpuStart(0x)", "RegionState(0x)"));
358 for (Idx
= 0; Idx
< Count
; ++Idx
) {
359 DEBUG ((DEBUG_VERBOSE
, "% 20Lx % 20Lx % 20Lx % 20Lx\n",
360 SmramMap
[Idx
].PhysicalStart
, SmramMap
[Idx
].PhysicalSize
,
361 SmramMap
[Idx
].CpuStart
, SmramMap
[Idx
].RegionState
));
366 GuidHob
= BuildGuidHob (&gEfiAcpiVariableGuid
,
367 sizeof SmramMap
[DescIdxSmmS3ResumeState
]);
368 if (GuidHob
== NULL
) {
369 return EFI_OUT_OF_RESOURCES
;
372 CopyMem (GuidHob
, &SmramMap
[DescIdxSmmS3ResumeState
],
373 sizeof SmramMap
[DescIdxSmmS3ResumeState
]);
376 // SmramAccessLock() depends on "mQ35SmramAtDefaultSmbase"; init the latter
377 // just before exposing the former via PEI_SMM_ACCESS_PPI.Lock().
379 InitQ35SmramAtDefaultSmbase ();
382 // We're done. The next step should succeed, but even if it fails, we can't
383 // roll back the above BuildGuidHob() allocation, because PEI doesn't support
386 return PeiServicesInstallPpi (mPpiList
);
390 // We really don't want to continue in this case.
394 return EFI_UNSUPPORTED
;