VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Library/PlatformInitLib/MemDetect.c

Last change on this file was 108794, checked in by vboxsync, 4 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 48.6 KB
Line 
1/**@file
2 Memory Detection for Virtual Machines.
3
4 Copyright (c) 2006 - 2024, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7Module Name:
8
9 MemDetect.c
10
11**/
12
13//
14// The package level header files this module uses
15//
16#include <IndustryStandard/E820.h>
17#include <IndustryStandard/I440FxPiix4.h>
18#include <IndustryStandard/Q35MchIch9.h>
19#include <IndustryStandard/CloudHv.h>
20#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
21#include <PiPei.h>
22#include <Register/Intel/SmramSaveStateMap.h>
23
24//
25// The Library classes this module consumes
26//
27#include <Library/BaseLib.h>
28#include <Library/BaseMemoryLib.h>
29#include <Library/CcProbeLib.h>
30#include <Library/DebugLib.h>
31#include <Library/HardwareInfoLib.h>
32#include <Library/HobLib.h>
33#include <Library/IoLib.h>
34#include <Library/MemEncryptSevLib.h>
35#include <Library/PcdLib.h>
36#include <Library/PciLib.h>
37#include <Library/PeimEntryPoint.h>
38#include <Library/ResourcePublicationLib.h>
39#include <Library/MtrrLib.h>
40#include <Library/QemuFwCfgLib.h>
41#include <Library/QemuFwCfgSimpleParserLib.h>
42#include <Library/TdxLib.h>
43
44#include <Library/PlatformInitLib.h>
45
46#include <Guid/AcpiS3Context.h>
47#include <Guid/SmramMemoryReserve.h>
48
49#ifdef VBOX
50# include "VBoxPkg.h"
51# include "DevEFI.h"
52# include "iprt/asm.h"
53
54static UINT32
55GetVmVariable(UINT32 Variable, CHAR8 *pbBuf, UINT32 cbBuf)
56{
57 UINT32 cbVar, offBuf;
58
59 ASMOutU32(EFI_INFO_PORT, Variable);
60 cbVar = ASMInU32(EFI_INFO_PORT);
61
62 for (offBuf = 0; offBuf < cbVar && offBuf < cbBuf; offBuf++)
63 pbBuf[offBuf] = ASMInU8(EFI_INFO_PORT);
64
65 return cbVar;
66}
67#endif
68
69#define MEGABYTE_SHIFT 20
70
71VOID
72EFIAPI
73PlatformQemuUc32BaseInitialization (
74 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
75 )
76{
77#ifdef VBOX
78 UINT64 McfgBase = 0;
79#endif
80
81 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
82 return;
83 }
84
85 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
86 PlatformInfoHob->Uc32Size = CLOUDHV_MMIO_HOLE_SIZE;
87 PlatformInfoHob->Uc32Base = CLOUDHV_MMIO_HOLE_ADDRESS;
88 return;
89 }
90
91 ASSERT (
92 PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID ||
93 PlatformInfoHob->HostBridgeDevId == INTEL_82441_DEVICE_ID
94 );
95
96 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
97
98#ifdef VBOX
99 /*
100 * Need to initialize the dynamic PcdPciExpressBaseAddress here after the low memory
101 * gets known or the UC base address will become wrong.
102 * The HOBs are not created however but this is done in PlatformMemMapInitialization()
103 * which is called later.
104 */
105 GetVmVariable(EFI_INFO_INDEX_MCFG_BASE, (CHAR8 *)&McfgBase, sizeof(McfgBase));
106 if (PlatformInfoHob->LowMemory < BASE_2GB)
107 PlatformInfoHob->LowMemory = BASE_2GB;
108 if (McfgBase == 0)
109 McfgBase = PlatformInfoHob->LowMemory; // backward compatibilit with old DevEFI
110
111 PcdSet64S (PcdPciExpressBaseAddress, McfgBase);
112#endif
113
114 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
115 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);
116 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) >= PlatformInfoHob->LowMemory);
117 }
118
119 //
120 // Start with the [LowerMemorySize, 4GB) range. Make sure one
121 // variable MTRR suffices by truncating the size to a whole power of two,
122 // while keeping the end affixed to 4GB. This will round the base up.
123 //
124 PlatformInfoHob->Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - PlatformInfoHob->LowMemory));
125 PlatformInfoHob->Uc32Base = (UINT32)(SIZE_4GB - PlatformInfoHob->Uc32Size);
126 //
127 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
128 // Therefore Uc32Base is at least 2GB.
129 //
130 ASSERT (PlatformInfoHob->Uc32Base >= BASE_2GB);
131
132 if (PlatformInfoHob->Uc32Base != PlatformInfoHob->LowMemory) {
133 DEBUG ((
134 DEBUG_VERBOSE,
135 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
136 "an UC32 size of 0x%x\n",
137 __func__,
138 PlatformInfoHob->LowMemory,
139 PlatformInfoHob->Uc32Base,
140 PlatformInfoHob->Uc32Size
141 ));
142 }
143}
144
145typedef VOID (*E820_SCAN_CALLBACK) (
146 EFI_E820_ENTRY64 *E820Entry,
147 EFI_HOB_PLATFORM_INFO *PlatformInfoHob
148 );
149
150STATIC
151EFI_STATUS
152PlatformScanE820Tdx (
153 IN E820_SCAN_CALLBACK Callback,
154 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
155 )
156{
157 EFI_E820_ENTRY64 E820Entry;
158 EFI_PEI_HOB_POINTERS Hob;
159
160 Hob.Raw = (UINT8 *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);
161
162 while (!END_OF_HOB_LIST (Hob)) {
163 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
164 if ((Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_UNACCEPTED) ||
165 (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY))
166 {
167 E820Entry.BaseAddr = Hob.ResourceDescriptor->PhysicalStart;
168 E820Entry.Length = Hob.ResourceDescriptor->ResourceLength;
169 E820Entry.Type = EfiAcpiAddressRangeMemory;
170 Callback (&E820Entry, PlatformInfoHob);
171 }
172 }
173
174 Hob.Raw = (UINT8 *)(Hob.Raw + Hob.Header->HobLength);
175 }
176
177 return EFI_SUCCESS;
178}
179
180/**
181 Store first address not used by e820 RAM entries in
182 PlatformInfoHob->FirstNonAddress
183**/
184STATIC
185VOID
186PlatformGetFirstNonAddressCB (
187 IN EFI_E820_ENTRY64 *E820Entry,
188 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
189 )
190{
191 UINT64 Candidate;
192
193 if (E820Entry->Type != EfiAcpiAddressRangeMemory) {
194 return;
195 }
196
197 Candidate = E820Entry->BaseAddr + E820Entry->Length;
198 if (PlatformInfoHob->FirstNonAddress < Candidate) {
199 DEBUG ((DEBUG_INFO, "%a: FirstNonAddress=0x%Lx\n", __func__, Candidate));
200 PlatformInfoHob->FirstNonAddress = Candidate;
201 }
202}
203
204/**
205 Store the low (below 4G) memory size in
206 PlatformInfoHob->LowMemory
207**/
208STATIC
209VOID
210PlatformGetLowMemoryCB (
211 IN EFI_E820_ENTRY64 *E820Entry,
212 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
213 )
214{
215 UINT64 Candidate;
216
217 if (E820Entry->Type != EfiAcpiAddressRangeMemory) {
218 return;
219 }
220
221 Candidate = E820Entry->BaseAddr + E820Entry->Length;
222 if (Candidate >= BASE_4GB) {
223 return;
224 }
225
226 if (PlatformInfoHob->LowMemory < Candidate) {
227 DEBUG ((DEBUG_INFO, "%a: LowMemory=0x%Lx\n", __func__, Candidate));
228 PlatformInfoHob->LowMemory = (UINT32)Candidate;
229 }
230}
231
232/**
233 Create HOBs for reservations and RAM (except low memory).
234**/
235STATIC
236VOID
237PlatformAddHobCB (
238 IN EFI_E820_ENTRY64 *E820Entry,
239 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
240 )
241{
242 UINT64 Base, End;
243
244 Base = E820Entry->BaseAddr;
245 End = E820Entry->BaseAddr + E820Entry->Length;
246
247 switch (E820Entry->Type) {
248 case EfiAcpiAddressRangeMemory:
249 if (Base >= BASE_4GB) {
250 //
251 // Round up the start address, and round down the end address.
252 //
253 Base = ALIGN_VALUE (Base, (UINT64)EFI_PAGE_SIZE);
254 End = End & ~(UINT64)EFI_PAGE_MASK;
255 if (Base < End) {
256 DEBUG ((DEBUG_INFO, "%a: HighMemory [0x%Lx, 0x%Lx)\n", __func__, Base, End));
257 PlatformAddMemoryRangeHob (Base, End);
258 }
259 }
260
261 break;
262 case EfiAcpiAddressRangeReserved:
263 BuildResourceDescriptorHob (EFI_RESOURCE_MEMORY_RESERVED, 0, Base, End - Base);
264 DEBUG ((DEBUG_INFO, "%a: Reserved [0x%Lx, 0x%Lx)\n", __func__, Base, End));
265 break;
266 default:
267 DEBUG ((
268 DEBUG_WARN,
269 "%a: Type %u [0x%Lx, 0x%Lx) (NOT HANDLED)\n",
270 __func__,
271 E820Entry->Type,
272 Base,
273 End
274 ));
275 break;
276 }
277}
278
279/**
280 Check whenever the 64bit PCI MMIO window overlaps with a reservation
281 from qemu. If so move down the MMIO window to resolve the conflict.
282
283 This happens on (virtual) AMD machines with 1TB address space,
284 because the AMD IOMMU uses an address window just below 1TB.
285**/
286STATIC
287VOID
288PlatformReservationConflictCB (
289 IN EFI_E820_ENTRY64 *E820Entry,
290 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
291 )
292{
293 UINT64 IntersectionBase;
294 UINT64 IntersectionEnd;
295 UINT64 NewBase;
296
297 IntersectionBase = MAX (
298 E820Entry->BaseAddr,
299 PlatformInfoHob->PcdPciMmio64Base
300 );
301 IntersectionEnd = MIN (
302 E820Entry->BaseAddr + E820Entry->Length,
303 PlatformInfoHob->PcdPciMmio64Base +
304 PlatformInfoHob->PcdPciMmio64Size
305 );
306
307 if (IntersectionBase >= IntersectionEnd) {
308 return; // no overlap
309 }
310
311 NewBase = E820Entry->BaseAddr - PlatformInfoHob->PcdPciMmio64Size;
312 NewBase = NewBase & ~(PlatformInfoHob->PcdPciMmio64Size - 1);
313
314 DEBUG ((
315 DEBUG_INFO,
316 "%a: move mmio: 0x%Lx => %Lx\n",
317 __func__,
318 PlatformInfoHob->PcdPciMmio64Base,
319 NewBase
320 ));
321 PlatformInfoHob->PcdPciMmio64Base = NewBase;
322}
323
324/**
325 Returns PVH memmap
326 @param Entries Pointer to PVH memmap
327 @param Count Number of entries
328 @return EFI_STATUS
329**/
330EFI_STATUS
331GetPvhMemmapEntries (
332 struct hvm_memmap_table_entry **Entries,
333 UINT32 *Count
334 )
335{
336 UINT32 *PVHResetVectorData;
337 struct hvm_start_info *pvh_start_info;
338
339 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);
340 if (PVHResetVectorData == 0) {
341 return EFI_NOT_FOUND;
342 }
343
344 pvh_start_info = (struct hvm_start_info *)(UINTN)PVHResetVectorData[0];
345
346 *Entries = (struct hvm_memmap_table_entry *)(UINTN)pvh_start_info->memmap_paddr;
347 *Count = pvh_start_info->memmap_entries;
348
349 return EFI_SUCCESS;
350}
351
352STATIC
353EFI_STATUS
354PlatformScanE820Pvh (
355 IN E820_SCAN_CALLBACK Callback,
356 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
357 )
358{
359 struct hvm_memmap_table_entry *Memmap;
360 UINT32 MemmapEntriesCount;
361 struct hvm_memmap_table_entry *Entry;
362 EFI_E820_ENTRY64 E820Entry;
363 EFI_STATUS Status;
364 UINT32 Loop;
365
366 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);
367 if (EFI_ERROR (Status)) {
368 return Status;
369 }
370
371 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {
372 Entry = Memmap + Loop;
373
374 if (Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) {
375 E820Entry.BaseAddr = Entry->addr;
376 E820Entry.Length = Entry->size;
377 E820Entry.Type = Entry->type;
378 Callback (&E820Entry, PlatformInfoHob);
379 }
380 }
381
382 return EFI_SUCCESS;
383}
384
385/**
386 Iterate over the entries in QEMU's fw_cfg E820 RAM map, call the
387 passed callback for each entry.
388
389 @param[in] Callback The callback function to be called.
390
391 @param[in out] PlatformInfoHob PlatformInfo struct which is passed
392 through to the callback.
393
394 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
395
396 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
397 whole multiple of sizeof(EFI_E820_ENTRY64). No
398 RAM entry was processed.
399
400 @return Error codes from QemuFwCfgFindFile(). No RAM
401 entry was processed.
402**/
403STATIC
404EFI_STATUS
405PlatformScanE820 (
406 IN E820_SCAN_CALLBACK Callback,
407 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
408 )
409{
410 EFI_STATUS Status;
411 FIRMWARE_CONFIG_ITEM FwCfgItem;
412 UINTN FwCfgSize;
413 EFI_E820_ENTRY64 E820Entry;
414 UINTN Processed;
415
416 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
417 return PlatformScanE820Pvh (Callback, PlatformInfoHob);
418 }
419
420 if (TdIsEnabled ()) {
421 return PlatformScanE820Tdx (Callback, PlatformInfoHob);
422 }
423
424 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);
425 if (EFI_ERROR (Status)) {
426 return Status;
427 }
428
429 if (FwCfgSize % sizeof E820Entry != 0) {
430 return EFI_PROTOCOL_ERROR;
431 }
432
433 QemuFwCfgSelectItem (FwCfgItem);
434 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {
435 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);
436 Callback (&E820Entry, PlatformInfoHob);
437 }
438
439 return EFI_SUCCESS;
440}
441
442STATIC
443UINT64
444GetHighestSystemMemoryAddressFromPvhMemmap (
445 BOOLEAN Below4gb
446 )
447{
448 struct hvm_memmap_table_entry *Memmap;
449 UINT32 MemmapEntriesCount;
450 struct hvm_memmap_table_entry *Entry;
451 EFI_STATUS Status;
452 UINT32 Loop;
453 UINT64 HighestAddress;
454 UINT64 EntryEnd;
455
456 HighestAddress = 0;
457
458 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);
459 ASSERT_EFI_ERROR (Status);
460
461 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {
462 Entry = Memmap + Loop;
463 EntryEnd = Entry->addr + Entry->size;
464
465 if ((Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) &&
466 (EntryEnd > HighestAddress))
467 {
468 if (Below4gb && (EntryEnd <= BASE_4GB)) {
469 HighestAddress = EntryEnd;
470 } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {
471 HighestAddress = EntryEnd;
472 }
473 }
474 }
475
476 return HighestAddress;
477}
478
479VOID
480EFIAPI
481PlatformGetSystemMemorySizeBelow4gb (
482 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
483 )
484{
485 EFI_STATUS Status;
486 UINT8 Cmos0x34;
487 UINT8 Cmos0x35;
488
489 if ((PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) &&
490 (CcProbe () != CcGuestTypeIntelTdx))
491 {
492 // Get the information from PVH memmap
493 PlatformInfoHob->LowMemory = (UINT32)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE);
494 return;
495 }
496
497 Status = PlatformScanE820 (PlatformGetLowMemoryCB, PlatformInfoHob);
498 if (!EFI_ERROR (Status) && (PlatformInfoHob->LowMemory > 0)) {
499 return;
500 }
501
502 //
503 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
504 // * CMOS(0x35) is the high byte
505 // * CMOS(0x34) is the low byte
506 // * The size is specified in 64kb chunks
507 // * Since this is memory above 16MB, the 16MB must be added
508 // into the calculation to get the total memory size.
509 //
510
511 Cmos0x34 = (UINT8)PlatformCmosRead8 (0x34);
512 Cmos0x35 = (UINT8)PlatformCmosRead8 (0x35);
513
514 PlatformInfoHob->LowMemory = (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
515}
516
517STATIC
518UINT64
519PlatformGetSystemMemorySizeAbove4gb (
520 )
521{
522 UINT32 Size;
523 UINTN CmosIndex;
524
525 //
526 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
527 // * CMOS(0x5d) is the most significant size byte
528 // * CMOS(0x5c) is the middle size byte
529 // * CMOS(0x5b) is the least significant size byte
530 // * The size is specified in 64kb chunks
531 //
532
533 Size = 0;
534 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {
535 Size = (UINT32)(Size << 8) + (UINT32)PlatformCmosRead8 (CmosIndex);
536 }
537
538 return LShiftU64 (Size, 16);
539}
540
541/**
542 Return the highest address that DXE could possibly use, plus one.
543**/
544STATIC
545VOID
546PlatformGetFirstNonAddress (
547 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
548 )
549{
550 UINT32 FwCfgPciMmio64Mb;
551 EFI_STATUS Status;
552 FIRMWARE_CONFIG_ITEM FwCfgItem;
553 UINTN FwCfgSize;
554 UINT64 HotPlugMemoryEnd;
555
556 //
557 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
558 // address from it. This can express an address >= 4GB+1TB.
559 //
560 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
561 // can only express a size smaller than 1TB), and add it to 4GB.
562 //
563 PlatformInfoHob->FirstNonAddress = BASE_4GB;
564 Status = PlatformScanE820 (PlatformGetFirstNonAddressCB, PlatformInfoHob);
565 if (EFI_ERROR (Status)) {
566 PlatformInfoHob->FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();
567 }
568
569 //
570 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
571 // resources to 32-bit anyway. See DegradeResource() in
572 // "PciResourceSupport.c".
573 //
574 #ifdef MDE_CPU_IA32
575 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
576 return;
577 }
578
579 #endif
580
581 //
582 // See if the user specified the number of megabytes for the 64-bit PCI host
583 // aperture. Accept an aperture size up to 16TB.
584 //
585 // As signaled by the "X-" prefix, this knob is experimental, and might go
586 // away at any time.
587 //
588 Status = QemuFwCfgParseUint32 (
589 "opt/ovmf/X-PciMmio64Mb",
590 FALSE,
591 &FwCfgPciMmio64Mb
592 );
593 switch (Status) {
594 case EFI_UNSUPPORTED:
595 case EFI_NOT_FOUND:
596 break;
597 case EFI_SUCCESS:
598 if (FwCfgPciMmio64Mb <= 0x1000000) {
599 PlatformInfoHob->PcdPciMmio64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);
600 break;
601 }
602
603 //
604 // fall through
605 //
606 default:
607 DEBUG ((
608 DEBUG_WARN,
609 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
610 __func__
611 ));
612 break;
613 }
614
615 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
616 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
617 DEBUG ((
618 DEBUG_INFO,
619 "%a: disabling 64-bit PCI host aperture\n",
620 __func__
621 ));
622 }
623
624 //
625 // There's nothing more to do; the amount of memory above 4GB fully
626 // determines the highest address plus one. The memory hotplug area (see
627 // below) plays no role for the firmware in this case.
628 //
629 return;
630 }
631
632 //
633 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
634 // absolute, exclusive end address for the memory hotplug area. This area
635 // starts right at the end of the memory above 4GB. The 64-bit PCI host
636 // aperture must be placed above it.
637 //
638 Status = QemuFwCfgFindFile (
639 "etc/reserved-memory-end",
640 &FwCfgItem,
641 &FwCfgSize
642 );
643 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {
644 QemuFwCfgSelectItem (FwCfgItem);
645 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);
646 DEBUG ((
647 DEBUG_VERBOSE,
648 "%a: HotPlugMemoryEnd=0x%Lx\n",
649 __func__,
650 HotPlugMemoryEnd
651 ));
652
653 ASSERT (HotPlugMemoryEnd >= PlatformInfoHob->FirstNonAddress);
654 PlatformInfoHob->FirstNonAddress = HotPlugMemoryEnd;
655 }
656
657 //
658 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
659 // that the host can map it with 1GB hugepages. Follow suit.
660 //
661 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->FirstNonAddress, (UINT64)SIZE_1GB);
662 PlatformInfoHob->PcdPciMmio64Size = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Size, (UINT64)SIZE_1GB);
663
664 //
665 // The 64-bit PCI host aperture should also be "naturally" aligned. The
666 // alignment is determined by rounding the size of the aperture down to the
667 // next smaller or equal power of two. That is, align the aperture by the
668 // largest BAR size that can fit into it.
669 //
670 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Base, GetPowerOfTwo64 (PlatformInfoHob->PcdPciMmio64Size));
671
672 //
673 // The useful address space ends with the 64-bit PCI host aperture.
674 //
675 PlatformInfoHob->FirstNonAddress = PlatformInfoHob->PcdPciMmio64Base + PlatformInfoHob->PcdPciMmio64Size;
676 return;
677}
678
679/*
680 * Use CPUID to figure physical address width.
681 *
682 * Does *not* work reliable on qemu. For historical reasons qemu
683 * returns phys-bits=40 by default even in case the host machine
684 * supports less than that.
685 *
686 * So we apply the following rules (which can be enabled/disabled
687 * using the QemuQuirk parameter) to figure whenever we can work with
688 * the returned physical address width or not:
689 *
690 * (1) If it is 41 or higher consider it valid.
691 * (2) If it is 40 or lower consider it valid in case it matches a
692 * known-good value for the CPU vendor, which is:
693 * -> 36 or 39 for Intel
694 * -> 40 for AMD
695 * (3) Otherwise consider it invalid.
696 *
697 * Recommendation: Run qemu with host-phys-bits=on. That will make
698 * sure guest phys-bits is not larger than host phys-bits. Some
699 * distro builds do that by default.
700 */
701VOID
702EFIAPI
703PlatformAddressWidthFromCpuid (
704 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob,
705 IN BOOLEAN QemuQuirk
706 )
707{
708 UINT32 RegEax, RegEbx, RegEcx, RegEdx, Max;
709 UINT8 PhysBits;
710 UINT8 GuestPhysBits;
711 CHAR8 Signature[13];
712 IA32_CR4 Cr4;
713 BOOLEAN Valid = FALSE;
714 BOOLEAN Page1GSupport = FALSE;
715
716 ZeroMem (Signature, sizeof (Signature));
717
718 AsmCpuid (0x80000000, &RegEax, &RegEbx, &RegEcx, &RegEdx);
719 *(UINT32 *)(Signature + 0) = RegEbx;
720 *(UINT32 *)(Signature + 4) = RegEdx;
721 *(UINT32 *)(Signature + 8) = RegEcx;
722 Max = RegEax;
723
724 if (Max >= 0x80000001) {
725 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
726 if ((RegEdx & BIT26) != 0) {
727 Page1GSupport = TRUE;
728 }
729 }
730
731 if (Max >= 0x80000008) {
732 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
733 PhysBits = (UINT8)RegEax;
734 GuestPhysBits = (UINT8)(RegEax >> 16);
735 } else {
736 PhysBits = 36;
737 GuestPhysBits = 0;
738 }
739
740 if (!QemuQuirk) {
741 Valid = TRUE;
742 } else if (GuestPhysBits) {
743 Valid = TRUE;
744 } else if (PhysBits >= 41) {
745 Valid = TRUE;
746 } else if (AsciiStrCmp (Signature, "GenuineIntel") == 0) {
747 if ((PhysBits == 36) || (PhysBits == 39)) {
748 Valid = TRUE;
749 }
750 } else if (AsciiStrCmp (Signature, "AuthenticAMD") == 0) {
751 if (PhysBits == 40) {
752 Valid = TRUE;
753 }
754 }
755
756 Cr4.UintN = AsmReadCr4 ();
757
758 DEBUG ((
759 DEBUG_INFO,
760 "%a: Signature: '%a', PhysBits: %d, GuestPhysBits: %d, QemuQuirk: %a, la57: %a, Valid: %a\n",
761 __func__,
762 Signature,
763 PhysBits,
764 GuestPhysBits,
765 QemuQuirk ? "On" : "Off",
766 Cr4.Bits.LA57 ? "On" : "Off",
767 Valid ? "Yes" : "No"
768 ));
769
770 if (GuestPhysBits && (PhysBits > GuestPhysBits)) {
771 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to %d (GuestPhysBits)\n", __func__, GuestPhysBits));
772 PhysBits = GuestPhysBits;
773 }
774
775 if (Valid) {
776 /*
777 * Due to the sign extension we can use only the lower half of the
778 * virtual address space to identity-map physical address space,
779 * which gives us a 47 bit wide address space with 4 paging levels
780 * and a 56 bit wide address space with 5 paging levels.
781 */
782 if (Cr4.Bits.LA57) {
783 if ((PhysBits > 48) && !GuestPhysBits) {
784 /*
785 * Some Intel CPUs support 5-level paging, have more than 48
786 * phys-bits but support only 4-level EPT, which effectively
787 * limits guest phys-bits to 48.
788 *
789 * AMD Processors have a different but somewhat related
790 * problem: They can handle guest phys-bits larger than 48
791 * only in case the host runs in 5-level paging mode.
792 *
793 * GuestPhysBits is used to communicate that kind of
794 * limitations from hypervisor to guest. If GuestPhysBits is
795 * not set play safe and limit phys-bits to 48.
796 */
797 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 48 (5-level paging, no GuestPhysBits)\n", __func__));
798 PhysBits = 48;
799 }
800 } else {
801 if (PhysBits > 46) {
802 /*
803 * Some older linux kernels apparently have problems handling
804 * phys-bits > 46 correctly, so use that instead of 47 as
805 * limit.
806 */
807 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 46 (4-level paging)\n", __func__));
808 PhysBits = 46;
809 }
810 }
811
812 if (!Page1GSupport && (PhysBits > 40)) {
813 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 40 (no 1G pages available)\n", __func__));
814 PhysBits = 40;
815 }
816
817 if (!FixedPcdGetBool (PcdUse1GPageTable) && (PhysBits > 40)) {
818 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 40 (PcdUse1GPageTable is false)\n", __func__));
819 PhysBits = 40;
820 }
821
822 PlatformInfoHob->PhysMemAddressWidth = PhysBits;
823 PlatformInfoHob->FirstNonAddress = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);
824 }
825}
826
827VOID
828EFIAPI
829PlatformDynamicMmioWindow (
830 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
831 )
832{
833 UINT64 AddrSpace, MmioSpace;
834
835 AddrSpace = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);
836 MmioSpace = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth - 3);
837
838 if ((PlatformInfoHob->PcdPciMmio64Size < MmioSpace) &&
839 (PlatformInfoHob->PcdPciMmio64Base + MmioSpace < AddrSpace))
840 {
841 DEBUG ((DEBUG_INFO, "%a: using dynamic mmio window\n", __func__));
842 DEBUG ((DEBUG_INFO, "%a: Addr Space 0x%Lx (%Ld GB)\n", __func__, AddrSpace, RShiftU64 (AddrSpace, 30)));
843 DEBUG ((DEBUG_INFO, "%a: MMIO Space 0x%Lx (%Ld GB)\n", __func__, MmioSpace, RShiftU64 (MmioSpace, 30)));
844 PlatformInfoHob->PcdPciMmio64Size = MmioSpace;
845 PlatformInfoHob->PcdPciMmio64Base = AddrSpace - MmioSpace;
846 PlatformScanE820 (PlatformReservationConflictCB, PlatformInfoHob);
847 } else {
848 DEBUG ((DEBUG_INFO, "%a: using classic mmio window\n", __func__));
849 }
850
851 DEBUG ((DEBUG_INFO, "%a: Pci64 Base 0x%Lx\n", __func__, PlatformInfoHob->PcdPciMmio64Base));
852 DEBUG ((DEBUG_INFO, "%a: Pci64 Size 0x%Lx\n", __func__, PlatformInfoHob->PcdPciMmio64Size));
853}
854
855/**
856 Iterate over the PCI host bridges resources information optionally provided
857 in fw-cfg and find the highest address contained in the PCI MMIO windows. If
858 the information is found, return the exclusive end; one past the last usable
859 address.
860
861 @param[out] PciMmioAddressEnd Pointer to one-after End Address updated with
862 information extracted from host-provided data
863 or zero if no information available or an
864 error happened
865
866 @retval EFI_SUCCESS PCI information was read and the output
867 parameter updated with the last valid
868 address in the 64-bit MMIO range.
869 @retval EFI_INVALID_PARAMETER Pointer parameter is invalid
870 @retval EFI_INCOMPATIBLE_VERSION Hardware information found in fw-cfg
871 has an incompatible format
872 @retval EFI_UNSUPPORTED Fw-cfg is not supported, thus host
873 provided information, if any, cannot be
874 read
875 @retval EFI_NOT_FOUND No PCI host bridge information provided
876 by the host.
877**/
878STATIC
879EFI_STATUS
880PlatformScanHostProvided64BitPciMmioEnd (
881 OUT UINT64 *PciMmioAddressEnd
882 )
883{
884 EFI_STATUS Status;
885 HOST_BRIDGE_INFO HostBridge;
886 FIRMWARE_CONFIG_ITEM FwCfgItem;
887 UINTN FwCfgSize;
888 UINTN FwCfgReadIndex;
889 UINTN ReadDataSize;
890 UINT64 Above4GMmioEnd;
891
892 if (PciMmioAddressEnd == NULL) {
893 return EFI_INVALID_PARAMETER;
894 }
895
896 *PciMmioAddressEnd = 0;
897 Above4GMmioEnd = 0;
898
899 Status = QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem, &FwCfgSize);
900 if (EFI_ERROR (Status)) {
901 return Status;
902 }
903
904 QemuFwCfgSelectItem (FwCfgItem);
905
906 FwCfgReadIndex = 0;
907 while (FwCfgReadIndex < FwCfgSize) {
908 Status = QemuFwCfgReadNextHardwareInfoByType (
909 HardwareInfoTypeHostBridge,
910 sizeof (HostBridge),
911 FwCfgSize,
912 &HostBridge,
913 &ReadDataSize,
914 &FwCfgReadIndex
915 );
916
917 if (Status != EFI_SUCCESS) {
918 //
919 // No more data available to read in the file, break
920 // loop and finish process
921 //
922 break;
923 }
924
925 Status = HardwareInfoPciHostBridgeLastMmioAddress (
926 &HostBridge,
927 ReadDataSize,
928 TRUE,
929 &Above4GMmioEnd
930 );
931
932 if (Status != EFI_SUCCESS) {
933 //
934 // Error parsing MMIO apertures and extracting last MMIO
935 // address, reset PciMmioAddressEnd as if no information was
936 // found, to avoid moving forward with incomplete data, and
937 // bail out
938 //
939 DEBUG ((
940 DEBUG_ERROR,
941 "%a: ignoring malformed hardware information from fw_cfg\n",
942 __func__
943 ));
944 *PciMmioAddressEnd = 0;
945 return Status;
946 }
947
948 if (Above4GMmioEnd > *PciMmioAddressEnd) {
949 *PciMmioAddressEnd = Above4GMmioEnd;
950 }
951 }
952
953 if (*PciMmioAddressEnd > 0) {
954 //
955 // Host-provided PCI information was found and a MMIO window end
956 // derived from it.
957 // Increase the End address by one to have the output pointing to
958 // one after the address in use (exclusive end).
959 //
960 *PciMmioAddressEnd += 1;
961
962 DEBUG ((
963 DEBUG_INFO,
964 "%a: Pci64End=0x%Lx\n",
965 __func__,
966 *PciMmioAddressEnd
967 ));
968
969 return EFI_SUCCESS;
970 }
971
972 return EFI_NOT_FOUND;
973}
974
975VOID
976EFIAPI
977Switch4Level (
978 VOID
979 );
980
981/**
982 Configure x64 paging levels.
983
984
985 The OVMF ResetVector code will enter long mode with 5-level paging if the
986 following conditions are true:
987
988 (1) OVMF has been built with PcdUse5LevelPageTable = TRUE, and
989 (2) the CPU supports 5-level paging (aka la57), and
990 (3) the CPU supports gigabyte pages, and
991 (4) the VM is not running in SEV mode.
992
993 Condition (4) is a temporary stopgap for BaseMemEncryptSevLib not supporting
994 5-level paging yet.
995
996
997 This function looks at the virtual machine configuration, then decides
998 whenever it will continue to use 5-level paging or downgrade to 4-level
999 paging for better compatibility with older guest OS versions.
1000
1001 There is a fw_cfg config option to explicitly request 4 or 5-level paging
1002 using 'qemu -fw_cfg name=opt/org.tianocode/PagingLevel,string=4|5'. If the
1003 option is present the requested paging level will be used.
1004
1005 Should that not be the case the function checks the size of the address space
1006 needed, which is the RAM installed plus fw_cfg reservations. The downgrade
1007 to 4-level paging will happen for small guests where the address space needed
1008 is lower than 1TB.
1009
1010
1011 This function will also log the paging level used and the reason for that.
1012**/
1013STATIC
1014VOID
1015PlatformSetupPagingLevel (
1016 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1017 )
1018{
1019 #ifdef MDE_CPU_X64
1020 UINT32 PagingLevel;
1021 EFI_STATUS Status;
1022 IA32_CR4 Cr4;
1023
1024 Cr4.UintN = AsmReadCr4 ();
1025 if (!Cr4.Bits.LA57) {
1026 /* The OvmfPkg ResetVector has NOT turned on 5-level paging, log the reason. */
1027 if (!PcdGetBool (PcdUse5LevelPageTable)) {
1028 DEBUG ((DEBUG_INFO, "%a: using 4-level paging (PcdUse5LevelPageTable disabled)\n", __func__));
1029 } else {
1030 DEBUG ((DEBUG_INFO, "%a: using 4-level paging (la57 not supported by cpu)\n", __func__));
1031 }
1032
1033 return;
1034 }
1035
1036 Status = QemuFwCfgParseUint32 (
1037 "opt/org.tianocode/PagingLevel",
1038 FALSE,
1039 &PagingLevel
1040 );
1041 switch (Status) {
1042 case EFI_NOT_FOUND:
1043 if (PlatformInfoHob->FirstNonAddress < (1ll << 40)) {
1044 //
1045 // If the highest address actually used is below 1TB switch back into
1046 // 4-level paging mode for better compatibility with older guests.
1047 //
1048 DEBUG ((DEBUG_INFO, "%a: using 4-level paging (default for small guest)\n", __func__));
1049 PagingLevel = 4;
1050 } else {
1051 DEBUG ((DEBUG_INFO, "%a: using 5-level paging (default for large guest)\n", __func__));
1052 PagingLevel = 5;
1053 }
1054
1055 break;
1056 case EFI_SUCCESS:
1057 if ((PagingLevel != 4) && (PagingLevel != 5)) {
1058 DEBUG ((DEBUG_INFO, "%a: invalid paging level in fw_cfg: %d\n", __func__, PagingLevel));
1059 return;
1060 }
1061
1062 DEBUG ((DEBUG_INFO, "%a: using %d-level paging (fw_cfg override)\n", __func__, PagingLevel));
1063 break;
1064 default:
1065 DEBUG ((DEBUG_WARN, "%a: QemuFwCfgParseUint32: %r\n", __func__, Status));
1066 return;
1067 }
1068
1069 if (PagingLevel == 4) {
1070 Switch4Level ();
1071 }
1072
1073 if (PagingLevel == 5) {
1074 /* The OvmfPkg ResetVector has turned on 5-level paging, nothing to do here. */
1075 }
1076
1077 #endif
1078}
1079
1080/**
1081 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
1082**/
1083VOID
1084EFIAPI
1085PlatformAddressWidthInitialization (
1086 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1087 )
1088{
1089 UINT8 PhysMemAddressWidth;
1090 EFI_STATUS Status;
1091
1092 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
1093 PlatformAddressWidthFromCpuid (PlatformInfoHob, FALSE);
1094 return;
1095 } else if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
1096 PlatformInfoHob->FirstNonAddress = BASE_4GB;
1097 Status = PlatformScanE820 (PlatformGetFirstNonAddressCB, PlatformInfoHob);
1098 if (EFI_ERROR (Status)) {
1099 PlatformInfoHob->FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();
1100 }
1101
1102 PlatformInfoHob->PcdPciMmio64Base = PlatformInfoHob->FirstNonAddress;
1103 PlatformAddressWidthFromCpuid (PlatformInfoHob, FALSE);
1104 PlatformInfoHob->PcdPciMmio64Size = PlatformInfoHob->FirstNonAddress - PlatformInfoHob->PcdPciMmio64Base;
1105
1106 return;
1107 }
1108
1109 //
1110 // First scan host-provided hardware information to assess if the address
1111 // space is already known. If so, guest must use those values.
1112 //
1113 Status = PlatformScanHostProvided64BitPciMmioEnd (&PlatformInfoHob->FirstNonAddress);
1114
1115 if (EFI_ERROR (Status)) {
1116 //
1117 // If the host did not provide valid hardware information leading to a
1118 // hard-defined 64-bit MMIO end, fold back to calculating the minimum range
1119 // needed.
1120 // As guest-physical memory size grows, the permanent PEI RAM requirements
1121 // are dominated by the identity-mapping page tables built by the DXE IPL.
1122 // The DXL IPL keys off of the physical address bits advertized in the CPU
1123 // HOB. To conserve memory, we calculate the minimum address width here.
1124 //
1125 PlatformGetFirstNonAddress (PlatformInfoHob);
1126 }
1127
1128 PlatformSetupPagingLevel (PlatformInfoHob);
1129
1130 PlatformAddressWidthFromCpuid (PlatformInfoHob, TRUE);
1131 if (PlatformInfoHob->PhysMemAddressWidth != 0) {
1132 // physical address width is known
1133 PlatformDynamicMmioWindow (PlatformInfoHob);
1134 return;
1135 }
1136
1137 //
1138 // physical address width is NOT known
1139 // -> do some guess work, mostly based on installed memory
1140 // -> try be conservstibe to stay below the guaranteed minimum of
1141 // 36 phys bits (aka 64 GB).
1142 //
1143 PhysMemAddressWidth = (UINT8)HighBitSet64 (PlatformInfoHob->FirstNonAddress);
1144
1145 //
1146 // If FirstNonAddress is not an integral power of two, then we need an
1147 // additional bit.
1148 //
1149 if ((PlatformInfoHob->FirstNonAddress & (PlatformInfoHob->FirstNonAddress - 1)) != 0) {
1150 ++PhysMemAddressWidth;
1151 }
1152
1153 //
1154 // The minimum address width is 36 (covers up to and excluding 64 GB, which
1155 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
1156 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
1157 // can simply assert that here, since 48 bits are good enough for 256 TB.
1158 //
1159 if (PhysMemAddressWidth <= 36) {
1160 PhysMemAddressWidth = 36;
1161 }
1162
1163 #if defined (MDE_CPU_X64)
1164 if (TdIsEnabled ()) {
1165 if (TdSharedPageMask () == (1ULL << 47)) {
1166 PhysMemAddressWidth = 48;
1167 } else {
1168 PhysMemAddressWidth = 52;
1169 }
1170 }
1171
1172 ASSERT (PhysMemAddressWidth <= 52);
1173 #else
1174 ASSERT (PhysMemAddressWidth <= 48);
1175 #endif
1176
1177 PlatformInfoHob->PhysMemAddressWidth = PhysMemAddressWidth;
1178}
1179
1180#ifndef VBOX
1181/**
1182 Create gEfiSmmSmramMemoryGuid HOB defined in the PI specification Vol. 3,
1183 section 5, which is used to describe the SMRAM memory regions supported
1184 by the platform.
1185
1186 @param[in] StartAddress StartAddress of smram.
1187 @param[in] Size Size of smram.
1188
1189**/
1190STATIC
1191VOID
1192CreateSmmSmramMemoryHob (
1193 IN EFI_PHYSICAL_ADDRESS StartAddress,
1194 IN UINT32 Size
1195 )
1196{
1197 UINTN BufferSize;
1198 UINT8 SmramRanges;
1199 EFI_PEI_HOB_POINTERS Hob;
1200 EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *SmramHobDescriptorBlock;
1201 VOID *GuidHob;
1202
1203 SmramRanges = 2;
1204 BufferSize = sizeof (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK) + (SmramRanges - 1) * sizeof (EFI_SMRAM_DESCRIPTOR);
1205
1206 Hob.Raw = BuildGuidHob (
1207 &gEfiSmmSmramMemoryGuid,
1208 BufferSize
1209 );
1210 ASSERT (Hob.Raw);
1211
1212 SmramHobDescriptorBlock = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *)(Hob.Raw);
1213 SmramHobDescriptorBlock->NumberOfSmmReservedRegions = SmramRanges;
1214
1215 //
1216 // 1. Create first SMRAM descriptor, which contains data structures used in S3 resume.
1217 // One page is enough for the data structure
1218 //
1219 SmramHobDescriptorBlock->Descriptor[0].PhysicalStart = StartAddress;
1220 SmramHobDescriptorBlock->Descriptor[0].CpuStart = StartAddress;
1221 SmramHobDescriptorBlock->Descriptor[0].PhysicalSize = EFI_PAGE_SIZE;
1222 SmramHobDescriptorBlock->Descriptor[0].RegionState = EFI_SMRAM_CLOSED | EFI_CACHEABLE | EFI_ALLOCATED;
1223
1224 //
1225 // 1.1 Create gEfiAcpiVariableGuid according SmramHobDescriptorBlock->Descriptor[0] since it's used in S3 resume.
1226 //
1227 GuidHob = BuildGuidHob (&gEfiAcpiVariableGuid, sizeof (EFI_SMRAM_DESCRIPTOR));
1228 ASSERT (GuidHob != NULL);
1229 CopyMem (GuidHob, &SmramHobDescriptorBlock->Descriptor[0], sizeof (EFI_SMRAM_DESCRIPTOR));
1230
1231 //
1232 // 2. Create second SMRAM descriptor, which is free and will be used by SMM foundation.
1233 //
1234 SmramHobDescriptorBlock->Descriptor[1].PhysicalStart = SmramHobDescriptorBlock->Descriptor[0].PhysicalStart + EFI_PAGE_SIZE;
1235 SmramHobDescriptorBlock->Descriptor[1].CpuStart = SmramHobDescriptorBlock->Descriptor[0].CpuStart + EFI_PAGE_SIZE;
1236 SmramHobDescriptorBlock->Descriptor[1].PhysicalSize = Size - EFI_PAGE_SIZE;
1237 SmramHobDescriptorBlock->Descriptor[1].RegionState = EFI_SMRAM_CLOSED | EFI_CACHEABLE;
1238}
1239
1240STATIC
1241VOID
1242QemuInitializeRamBelow1gb (
1243 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1244 )
1245{
1246 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
1247 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);
1248 PlatformAddReservedMemoryBaseSizeHob (
1249 SMM_DEFAULT_SMBASE,
1250 MCH_DEFAULT_SMBASE_SIZE,
1251 TRUE /* Cacheable */
1252 );
1253 STATIC_ASSERT (
1254 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,
1255 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
1256 );
1257 PlatformAddMemoryRangeHob (
1258 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,
1259 BASE_512KB + BASE_128KB
1260 );
1261 } else {
1262 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
1263 }
1264}
1265
1266/**
1267 Peform Memory Detection for QEMU / KVM
1268
1269**/
1270VOID
1271EFIAPI
1272PlatformQemuInitializeRam (
1273 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1274 )
1275{
1276 UINT64 UpperMemorySize;
1277 MTRR_SETTINGS MtrrSettings;
1278 EFI_STATUS Status;
1279
1280 DEBUG ((DEBUG_INFO, "%a called\n", __func__));
1281
1282 //
1283 // Determine total memory size available
1284 //
1285 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
1286
1287 //
1288 // CpuMpPei saves the original contents of the borrowed area in permanent
1289 // PEI RAM, in a backup buffer allocated with the normal PEI services.
1290 // CpuMpPei restores the original contents ("returns" the borrowed area) at
1291 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
1292 // transferring control to the OS's wakeup vector in the FACS.
1293 //
1294 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
1295 // restore the original contents. Furthermore, we expect all such PEIMs
1296 // (CpuMpPei included) to claim the borrowed areas by producing memory
1297 // allocation HOBs, and to honor preexistent memory allocation HOBs when
1298 // looking for an area to borrow.
1299 //
1300 QemuInitializeRamBelow1gb (PlatformInfoHob);
1301
1302 if (PlatformInfoHob->SmmSmramRequire) {
1303 UINT32 TsegSize;
1304 EFI_PHYSICAL_ADDRESS TsegBase;
1305
1306 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
1307 TsegBase = PlatformInfoHob->LowMemory - TsegSize;
1308 PlatformAddMemoryRangeHob (BASE_1MB, TsegBase);
1309 PlatformAddReservedMemoryBaseSizeHob (
1310 TsegBase,
1311 TsegSize,
1312 TRUE
1313 );
1314
1315 //
1316 // Create gEfiSmmSmramMemoryGuid HOB
1317 //
1318 CreateSmmSmramMemoryHob (TsegBase, TsegSize);
1319 } else {
1320 PlatformAddMemoryRangeHob (BASE_1MB, PlatformInfoHob->LowMemory);
1321 }
1322
1323 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
1324 //
1325 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
1326 // entries. Otherwise, create a single memory HOB with the flat >=4GB
1327 // memory size read from the CMOS.
1328 //
1329 Status = PlatformScanE820 (PlatformAddHobCB, PlatformInfoHob);
1330 if (EFI_ERROR (Status)) {
1331 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();
1332 if (UpperMemorySize != 0) {
1333 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
1334 }
1335 }
1336 }
1337
1338 //
1339 // We'd like to keep the following ranges uncached:
1340 // - [640 KB, 1 MB)
1341 // - [Uc32Base, 4 GB)
1342 //
1343 // Everything else should be WB. Unfortunately, programming the inverse (ie.
1344 // keeping the default UC, and configuring the complement set of the above as
1345 // WB) is not reliable in general, because the end of the upper RAM can have
1346 // practically any alignment, and we may not have enough variable MTRRs to
1347 // cover it exactly.
1348 //
1349 // Because of that PlatformQemuUc32BaseInitialization() will round
1350 // up PlatformInfoHob->LowMemory to make sure a single mtrr register
1351 // is enough. The the result will be stored in
1352 // PlatformInfoHob->Uc32Base. On a typical qemu configuration with
1353 // gigabyte-alignment being used LowMemory will be 2 or 3 GB and no
1354 // rounding is needed, so LowMemory and Uc32Base will be identical.
1355 //
1356 if (IsMtrrSupported () && (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID)) {
1357 MtrrGetAllMtrrs (&MtrrSettings);
1358
1359 //
1360 // See SecMtrrSetup(), default type should be write back
1361 //
1362 ASSERT ((MtrrSettings.MtrrDefType & BIT11) != 0);
1363 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
1364 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == MTRR_CACHE_WRITE_BACK);
1365
1366 //
1367 // flip default type to writeback
1368 //
1369 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, MTRR_CACHE_WRITE_BACK);
1370 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
1371 MtrrSettings.MtrrDefType |= BIT10;
1372 MtrrSetAllMtrrs (&MtrrSettings);
1373
1374 //
1375 // Set memory range from 640KB to 1MB to uncacheable
1376 //
1377 Status = MtrrSetMemoryAttribute (
1378 BASE_512KB + BASE_128KB,
1379 BASE_1MB - (BASE_512KB + BASE_128KB),
1380 CacheUncacheable
1381 );
1382 ASSERT_EFI_ERROR (Status);
1383
1384 //
1385 // Set the memory range from the start of the 32-bit PCI MMIO
1386 // aperture to 4GB as uncacheable.
1387 //
1388 Status = MtrrSetMemoryAttribute (
1389 PlatformInfoHob->Uc32Base,
1390 SIZE_4GB - PlatformInfoHob->Uc32Base,
1391 CacheUncacheable
1392 );
1393 ASSERT_EFI_ERROR (Status);
1394 }
1395}
1396#else
1397VOID
1398EFIAPI
1399PlatformQemuInitializeRam (
1400 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1401 )
1402{
1403 UINT64 UpperMemorySize;
1404
1405 DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));
1406
1407 //
1408 // Determine total memory size available
1409 //
1410 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
1411 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();
1412
1413 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
1414 //
1415 // Create the following memory HOB as an exception on the S3 boot path.
1416 //
1417 // Normally we'd create memory HOBs only on the normal boot path. However,
1418 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
1419 // well, for "borrowing" a subset of it temporarily, for the AP startup
1420 // vector.
1421 //
1422 // CpuMpPei saves the original contents of the borrowed area in permanent
1423 // PEI RAM, in a backup buffer allocated with the normal PEI services.
1424 // CpuMpPei restores the original contents ("returns" the borrowed area) at
1425 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
1426 // transferring control to the OS's wakeup vector in the FACS.
1427 //
1428 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
1429 // restore the original contents. Furthermore, we expect all such PEIMs
1430 // (CpuMpPei included) to claim the borrowed areas by producing memory
1431 // allocation HOBs, and to honor preexistent memory allocation HOBs when
1432 // looking for an area to borrow.
1433 //
1434 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
1435 } else {
1436 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
1437 }
1438 PlatformInfoHob->LowMemory -= BASE_64KB; /* Reserves 64KB for ACPI tables. */
1439
1440 //
1441 // Create memory HOBs
1442 //
1443 PlatformAddMemoryRangeHob (BASE_1MB, PlatformInfoHob->LowMemory);
1444 MtrrSetMemoryAttribute (BASE_1MB, PlatformInfoHob->LowMemory - BASE_1MB, CacheWriteBack);
1445 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
1446 MtrrSetMemoryAttribute (0, BASE_512KB + BASE_128KB, CacheWriteBack);
1447
1448 if (UpperMemorySize != 0) {
1449 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
1450
1451 MtrrSetMemoryAttribute (BASE_4GB, UpperMemorySize, CacheWriteBack);
1452 }
1453}
1454#endif
1455
1456VOID
1457EFIAPI
1458PlatformQemuInitializeRamForS3 (
1459 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1460 )
1461{
1462 if (PlatformInfoHob->S3Supported && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {
1463 //
1464 // This is the memory range that will be used for PEI on S3 resume
1465 //
1466 BuildMemoryAllocationHob (
1467 PlatformInfoHob->S3AcpiReservedMemoryBase,
1468 PlatformInfoHob->S3AcpiReservedMemorySize,
1469 EfiACPIMemoryNVS
1470 );
1471
1472 //
1473 // Cover the initial RAM area used as stack and temporary PEI heap.
1474 //
1475 // This is reserved as ACPI NVS so it can be used on S3 resume.
1476 //
1477 BuildMemoryAllocationHob (
1478 PcdGet32 (PcdOvmfSecPeiTempRamBase),
1479 PcdGet32 (PcdOvmfSecPeiTempRamSize),
1480 EfiACPIMemoryNVS
1481 );
1482
1483 //
1484 // SEC stores its table of GUIDed section handlers here.
1485 //
1486 BuildMemoryAllocationHob (
1487 PcdGet64 (PcdGuidedExtractHandlerTableAddress),
1488 PcdGet32 (PcdGuidedExtractHandlerTableSize),
1489 EfiACPIMemoryNVS
1490 );
1491
1492 #ifdef MDE_CPU_X64
1493 //
1494 // Reserve the initial page tables built by the reset vector code.
1495 //
1496 // Since this memory range will be used by the Reset Vector on S3
1497 // resume, it must be reserved as ACPI NVS.
1498 //
1499 BuildMemoryAllocationHob (
1500 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),
1501 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),
1502 EfiACPIMemoryNVS
1503 );
1504
1505 if (PlatformInfoHob->SevEsIsEnabled) {
1506 //
1507 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1508 // includes the extra page table used to break down the 2MB page
1509 // mapping into 4KB page entries where the GHCB resides and the
1510 // GHCB area itself.
1511 //
1512 // Since this memory range will be used by the Reset Vector on S3
1513 // resume, it must be reserved as ACPI NVS.
1514 //
1515 BuildMemoryAllocationHob (
1516 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),
1517 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),
1518 EfiACPIMemoryNVS
1519 );
1520 BuildMemoryAllocationHob (
1521 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),
1522 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),
1523 EfiACPIMemoryNVS
1524 );
1525 BuildMemoryAllocationHob (
1526 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),
1527 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),
1528 EfiACPIMemoryNVS
1529 );
1530 }
1531
1532 #endif
1533 }
1534
1535 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
1536#ifndef VBOX
1537 /*
1538 * This is currently not required and interferes with older OS X bootloaders trying to
1539 * allocate memory in that area. Needs to be revisited once we need this.
1540 */
1541 if (!PlatformInfoHob->SmmSmramRequire) {
1542 //
1543 // Reserve the lock box storage area
1544 //
1545 // Since this memory range will be used on S3 resume, it must be
1546 // reserved as ACPI NVS.
1547 //
1548 // If S3 is unsupported, then various drivers might still write to the
1549 // LockBox area. We ought to prevent DXE from serving allocation requests
1550 // such that they would overlap the LockBox storage.
1551 //
1552 ZeroMem (
1553 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
1554 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)
1555 );
1556 BuildMemoryAllocationHob (
1557 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
1558 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),
1559 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1560 );
1561 }
1562
1563 if (PlatformInfoHob->SmmSmramRequire) {
1564 UINT32 TsegSize;
1565
1566 //
1567 // Make sure the TSEG area that we reported as a reserved memory resource
1568 // cannot be used for reserved memory allocations.
1569 //
1570 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
1571 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
1572 BuildMemoryAllocationHob (
1573 PlatformInfoHob->LowMemory - TsegSize,
1574 TsegSize,
1575 EfiReservedMemoryType
1576 );
1577 //
1578 // Similarly, allocate away the (already reserved) SMRAM at the default
1579 // SMBASE, if it exists.
1580 //
1581 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {
1582 BuildMemoryAllocationHob (
1583 SMM_DEFAULT_SMBASE,
1584 MCH_DEFAULT_SMBASE_SIZE,
1585 EfiReservedMemoryType
1586 );
1587 }
1588 }
1589
1590 #ifdef MDE_CPU_X64
1591 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {
1592 //
1593 // Reserve the work area.
1594 //
1595 // Since this memory range will be used by the Reset Vector on S3
1596 // resume, it must be reserved as ACPI NVS.
1597 //
1598 // If S3 is unsupported, then various drivers might still write to the
1599 // work area. We ought to prevent DXE from serving allocation requests
1600 // such that they would overlap the work area.
1601 //
1602 BuildMemoryAllocationHob (
1603 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),
1604 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),
1605 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1606 );
1607 }
1608
1609 #endif
1610#endif
1611 }
1612}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette