2 Memory Detection for Virtual Machines.
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2019, Citrix Systems, Inc.
7 SPDX-License-Identifier: BSD-2-Clause-Patent
16 // The package level header files this module uses
18 #include <IndustryStandard/Q35MchIch9.h>
22 // The Library classes this module consumes
24 #include <Library/BaseLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/HobLib.h>
28 #include <Library/IoLib.h>
29 #include <Library/PcdLib.h>
30 #include <Library/PciLib.h>
31 #include <Library/PeimEntryPoint.h>
32 #include <Library/ResourcePublicationLib.h>
37 UINT8 mPhysMemAddressWidth
;
39 STATIC UINT32 mS3AcpiReservedMemoryBase
;
40 STATIC UINT32 mS3AcpiReservedMemorySize
;
42 STATIC UINT16 mQ35TsegMbytes
;
45 Q35TsegMbytesInitialization (
49 UINT16 ExtendedTsegMbytes
;
50 RETURN_STATUS PcdStatus
;
52 if (mHostBridgeDevId
!= INTEL_Q35_MCH_DEVICE_ID
) {
55 "%a: no TSEG (SMRAM) on host bridge DID=0x%04x; "
56 "only DID=0x%04x (Q35) is supported\n",
59 INTEL_Q35_MCH_DEVICE_ID
66 // Check if QEMU offers an extended TSEG.
68 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
69 // register, and reading back the register.
71 // On a QEMU machine type that does not offer an extended TSEG, the initial
72 // write overwrites whatever value a malicious guest OS may have placed in
73 // the (unimplemented) register, before entering S3 or rebooting.
74 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
76 // On a QEMU machine type that offers an extended TSEG, the initial write
77 // triggers an update to the register. Subsequently, the value read back
78 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
79 // number of megabytes.
81 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
), MCH_EXT_TSEG_MB_QUERY
);
82 ExtendedTsegMbytes
= PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB
));
83 if (ExtendedTsegMbytes
== MCH_EXT_TSEG_MB_QUERY
) {
84 mQ35TsegMbytes
= PcdGet16 (PcdQ35TsegMbytes
);
90 "%a: QEMU offers an extended TSEG (%d MB)\n",
94 PcdStatus
= PcdSet16S (PcdQ35TsegMbytes
, ExtendedTsegMbytes
);
95 ASSERT_RETURN_ERROR (PcdStatus
);
96 mQ35TsegMbytes
= ExtendedTsegMbytes
;
101 GetHighestSystemMemoryAddress (
105 EFI_E820_ENTRY64
*E820Map
;
106 UINT32 E820EntriesCount
;
107 EFI_E820_ENTRY64
*Entry
;
110 UINT64 HighestAddress
;
115 Status
= XenGetE820Map (&E820Map
, &E820EntriesCount
);
116 ASSERT_EFI_ERROR (Status
);
118 for (Loop
= 0; Loop
< E820EntriesCount
; Loop
++) {
119 Entry
= E820Map
+ Loop
;
120 EntryEnd
= Entry
->BaseAddr
+ Entry
->Length
;
122 if (Entry
->Type
== EfiAcpiAddressRangeMemory
&&
123 EntryEnd
> HighestAddress
) {
125 if (Below4gb
&& (EntryEnd
<= BASE_4GB
)) {
126 HighestAddress
= EntryEnd
;
127 } else if (!Below4gb
&& (EntryEnd
>= BASE_4GB
)) {
128 HighestAddress
= EntryEnd
;
134 // Round down the end address.
136 return HighestAddress
& ~(UINT64
)EFI_PAGE_MASK
;
140 GetSystemMemorySizeBelow4gb (
148 // In PVH case, there is no CMOS, we have to calculate the memory size
149 // from parsing the E820
151 if (XenPvhDetected ()) {
152 UINT64 HighestAddress
;
154 HighestAddress
= GetHighestSystemMemoryAddress (TRUE
);
155 ASSERT (HighestAddress
> 0 && HighestAddress
<= BASE_4GB
);
157 return HighestAddress
;
161 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
162 // * CMOS(0x35) is the high byte
163 // * CMOS(0x34) is the low byte
164 // * The size is specified in 64kb chunks
165 // * Since this is memory above 16MB, the 16MB must be added
166 // into the calculation to get the total memory size.
169 Cmos0x34
= (UINT8
) CmosRead8 (0x34);
170 Cmos0x35
= (UINT8
) CmosRead8 (0x35);
172 return (UINT32
) (((UINTN
)((Cmos0x35
<< 8) + Cmos0x34
) << 16) + SIZE_16MB
);
178 GetSystemMemorySizeAbove4gb (
185 // In PVH case, there is no CMOS, we have to calculate the memory size
186 // from parsing the E820
188 if (XenPvhDetected ()) {
189 UINT64 HighestAddress
;
191 HighestAddress
= GetHighestSystemMemoryAddress (FALSE
);
192 ASSERT (HighestAddress
== 0 || HighestAddress
>= BASE_4GB
);
194 if (HighestAddress
>= BASE_4GB
) {
195 HighestAddress
-= BASE_4GB
;
198 return HighestAddress
;
202 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
203 // * CMOS(0x5d) is the most significant size byte
204 // * CMOS(0x5c) is the middle size byte
205 // * CMOS(0x5b) is the least significant size byte
206 // * The size is specified in 64kb chunks
210 for (CmosIndex
= 0x5d; CmosIndex
>= 0x5b; CmosIndex
--) {
211 Size
= (UINT32
) (Size
<< 8) + (UINT32
) CmosRead8 (CmosIndex
);
214 return LShiftU64 (Size
, 16);
219 Return the highest address that DXE could possibly use, plus one.
227 UINT64 FirstNonAddress
;
228 UINT64 Pci64Base
, Pci64Size
;
229 RETURN_STATUS PcdStatus
;
231 FirstNonAddress
= BASE_4GB
+ GetSystemMemorySizeAbove4gb ();
234 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
235 // resources to 32-bit anyway. See DegradeResource() in
236 // "PciResourceSupport.c".
239 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
240 return FirstNonAddress
;
245 // Otherwise, in order to calculate the highest address plus one, we must
246 // consider the 64-bit PCI host aperture too. Fetch the default size.
248 Pci64Size
= PcdGet64 (PcdPciMmio64Size
);
250 if (Pci64Size
== 0) {
251 if (mBootMode
!= BOOT_ON_S3_RESUME
) {
252 DEBUG ((DEBUG_INFO
, "%a: disabling 64-bit PCI host aperture\n",
254 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, 0);
255 ASSERT_RETURN_ERROR (PcdStatus
);
259 // There's nothing more to do; the amount of memory above 4GB fully
260 // determines the highest address plus one. The memory hotplug area (see
261 // below) plays no role for the firmware in this case.
263 return FirstNonAddress
;
267 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
268 // that the host can map it with 1GB hugepages. Follow suit.
270 Pci64Base
= ALIGN_VALUE (FirstNonAddress
, (UINT64
)SIZE_1GB
);
271 Pci64Size
= ALIGN_VALUE (Pci64Size
, (UINT64
)SIZE_1GB
);
274 // The 64-bit PCI host aperture should also be "naturally" aligned. The
275 // alignment is determined by rounding the size of the aperture down to the
276 // next smaller or equal power of two. That is, align the aperture by the
277 // largest BAR size that can fit into it.
279 Pci64Base
= ALIGN_VALUE (Pci64Base
, GetPowerOfTwo64 (Pci64Size
));
281 if (mBootMode
!= BOOT_ON_S3_RESUME
) {
283 // The core PciHostBridgeDxe driver will automatically add this range to
284 // the GCD memory space map through our PciHostBridgeLib instance; here we
285 // only need to set the PCDs.
287 PcdStatus
= PcdSet64S (PcdPciMmio64Base
, Pci64Base
);
288 ASSERT_RETURN_ERROR (PcdStatus
);
289 PcdStatus
= PcdSet64S (PcdPciMmio64Size
, Pci64Size
);
290 ASSERT_RETURN_ERROR (PcdStatus
);
292 DEBUG ((DEBUG_INFO
, "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
293 __FUNCTION__
, Pci64Base
, Pci64Size
));
297 // The useful address space ends with the 64-bit PCI host aperture.
299 FirstNonAddress
= Pci64Base
+ Pci64Size
;
300 return FirstNonAddress
;
305 Initialize the mPhysMemAddressWidth variable, based on guest RAM size.
308 AddressWidthInitialization (
312 UINT64 FirstNonAddress
;
315 // As guest-physical memory size grows, the permanent PEI RAM requirements
316 // are dominated by the identity-mapping page tables built by the DXE IPL.
317 // The DXL IPL keys off of the physical address bits advertized in the CPU
318 // HOB. To conserve memory, we calculate the minimum address width here.
320 FirstNonAddress
= GetFirstNonAddress ();
321 mPhysMemAddressWidth
= (UINT8
)HighBitSet64 (FirstNonAddress
);
324 // If FirstNonAddress is not an integral power of two, then we need an
327 if ((FirstNonAddress
& (FirstNonAddress
- 1)) != 0) {
328 ++mPhysMemAddressWidth
;
332 // The minimum address width is 36 (covers up to and excluding 64 GB, which
333 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
334 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
335 // can simply assert that here, since 48 bits are good enough for 256 TB.
337 if (mPhysMemAddressWidth
<= 36) {
338 mPhysMemAddressWidth
= 36;
340 ASSERT (mPhysMemAddressWidth
<= 48);
345 Calculate the cap for the permanent PEI memory.
353 BOOLEAN Page1GSupport
;
361 // If DXE is 32-bit, then just return the traditional 64 MB cap.
364 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
370 // Dependent on physical address width, PEI memory allocations can be
371 // dominated by the page tables built for 64-bit DXE. So we key the cap off
372 // of those. The code below is based on CreateIdentityMappingPageTables() in
373 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
375 Page1GSupport
= FALSE
;
376 if (PcdGetBool (PcdUse1GPageTable
)) {
377 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
378 if (RegEax
>= 0x80000001) {
379 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
380 if ((RegEdx
& BIT26
) != 0) {
381 Page1GSupport
= TRUE
;
386 if (mPhysMemAddressWidth
<= 39) {
388 PdpEntries
= 1 << (mPhysMemAddressWidth
- 30);
389 ASSERT (PdpEntries
<= 0x200);
391 Pml4Entries
= 1 << (mPhysMemAddressWidth
- 39);
392 ASSERT (Pml4Entries
<= 0x200);
396 TotalPages
= Page1GSupport
? Pml4Entries
+ 1 :
397 (PdpEntries
+ 1) * Pml4Entries
+ 1;
398 ASSERT (TotalPages
<= 0x40201);
401 // Add 64 MB for miscellaneous allocations. Note that for
402 // mPhysMemAddressWidth values close to 36, the cap will actually be
403 // dominated by this increment.
405 return (UINT32
)(EFI_PAGES_TO_SIZE (TotalPages
) + SIZE_64MB
);
410 Publish PEI core memory
412 @return EFI_SUCCESS The PEIM initialized successfully.
421 EFI_PHYSICAL_ADDRESS MemoryBase
;
423 UINT32 LowerMemorySize
;
426 LowerMemorySize
= GetSystemMemorySizeBelow4gb ();
428 if (mBootMode
== BOOT_ON_S3_RESUME
) {
429 MemoryBase
= mS3AcpiReservedMemoryBase
;
430 MemorySize
= mS3AcpiReservedMemorySize
;
432 PeiMemoryCap
= GetPeiMemoryCap ();
433 DEBUG ((DEBUG_INFO
, "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
434 __FUNCTION__
, mPhysMemAddressWidth
, PeiMemoryCap
>> 10));
437 // Determine the range of memory to use during PEI
440 PcdGet32 (PcdOvmfDxeMemFvBase
) + PcdGet32 (PcdOvmfDxeMemFvSize
);
441 MemorySize
= LowerMemorySize
- MemoryBase
;
442 if (MemorySize
> PeiMemoryCap
) {
443 MemoryBase
= LowerMemorySize
- PeiMemoryCap
;
444 MemorySize
= PeiMemoryCap
;
449 // Publish this memory to the PEI Core
451 Status
= PublishSystemMemory(MemoryBase
, MemorySize
);
452 ASSERT_EFI_ERROR (Status
);
459 Publish system RAM and reserve memory regions
463 InitializeRamRegions (
467 XenPublishRamRegions ();
469 if (mBootMode
!= BOOT_ON_S3_RESUME
) {
471 // Reserve the lock box storage area
473 // Since this memory range will be used on S3 resume, it must be
474 // reserved as ACPI NVS.
476 // If S3 is unsupported, then various drivers might still write to the
477 // LockBox area. We ought to prevent DXE from serving allocation requests
478 // such that they would overlap the LockBox storage.
481 (VOID
*)(UINTN
) PcdGet32 (PcdOvmfLockBoxStorageBase
),
482 (UINTN
) PcdGet32 (PcdOvmfLockBoxStorageSize
)
484 BuildMemoryAllocationHob (
485 (EFI_PHYSICAL_ADDRESS
)(UINTN
) PcdGet32 (PcdOvmfLockBoxStorageBase
),
486 (UINT64
)(UINTN
) PcdGet32 (PcdOvmfLockBoxStorageSize
),