]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/PlatformInitLib/MemDetect.c
OvmfPkg/PlatformInitLib: Create MemDetect.c
[mirror_edk2.git] / OvmfPkg / Library / PlatformInitLib / MemDetect.c
1 /**@file
2 Memory Detection for Virtual Machines.
3
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 Module 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/DebugLib.h>
30 #include <Library/HobLib.h>
31 #include <Library/IoLib.h>
32 #include <Library/MemEncryptSevLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/PciLib.h>
35 #include <Library/PeimEntryPoint.h>
36 #include <Library/ResourcePublicationLib.h>
37 #include <Library/MtrrLib.h>
38 #include <Library/QemuFwCfgLib.h>
39 #include <Library/QemuFwCfgSimpleParserLib.h>
40 #include <Library/PlatformInitLib.h>
41
42 VOID
43 EFIAPI
44 PlatformQemuUc32BaseInitialization (
45 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
46 )
47 {
48 UINT32 LowerMemorySize;
49
50 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
51 return;
52 }
53
54 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
55 //
56 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
57 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
58 // setting PcdPciExpressBaseAddress such that describing the
59 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
60 // variable MTRRs (preferably 1 or 2).
61 //
62 ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);
63 PlatformInfoHob->Uc32Base = (UINT32)FixedPcdGet64 (PcdPciExpressBaseAddress);
64 return;
65 }
66
67 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
68 PlatformInfoHob->Uc32Size = CLOUDHV_MMIO_HOLE_SIZE;
69 PlatformInfoHob->Uc32Base = CLOUDHV_MMIO_HOLE_ADDRESS;
70 return;
71 }
72
73 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_82441_DEVICE_ID);
74 //
75 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
76 // variable MTRR suffices by truncating the size to a whole power of two,
77 // while keeping the end affixed to 4GB. This will round the base up.
78 //
79 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
80 PlatformInfoHob->Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - LowerMemorySize));
81 PlatformInfoHob->Uc32Base = (UINT32)(SIZE_4GB - PlatformInfoHob->Uc32Size);
82 //
83 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
84 // Therefore Uc32Base is at least 2GB.
85 //
86 ASSERT (PlatformInfoHob->Uc32Base >= BASE_2GB);
87
88 if (PlatformInfoHob->Uc32Base != LowerMemorySize) {
89 DEBUG ((
90 DEBUG_VERBOSE,
91 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
92 "an UC32 size of 0x%x\n",
93 __FUNCTION__,
94 LowerMemorySize,
95 PlatformInfoHob->Uc32Base,
96 PlatformInfoHob->Uc32Size
97 ));
98 }
99 }
100
101 /**
102 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside
103 of the 32-bit address range.
104
105 Find the highest exclusive >=4GB RAM address, or produce memory resource
106 descriptor HOBs for RAM entries that start at or above 4GB.
107
108 @param[out] MaxAddress If MaxAddress is NULL, then PlatformScanOrAdd64BitE820Ram()
109 produces memory resource descriptor HOBs for RAM
110 entries that start at or above 4GB.
111
112 Otherwise, MaxAddress holds the highest exclusive
113 >=4GB RAM address on output. If QEMU's fw_cfg E820
114 RAM map contains no RAM entry that starts outside of
115 the 32-bit address range, then MaxAddress is exactly
116 4GB on output.
117
118 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
119
120 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
121 whole multiple of sizeof(EFI_E820_ENTRY64). No
122 RAM entry was processed.
123
124 @return Error codes from QemuFwCfgFindFile(). No RAM
125 entry was processed.
126 **/
127 STATIC
128 EFI_STATUS
129 PlatformScanOrAdd64BitE820Ram (
130 IN BOOLEAN AddHighHob,
131 OUT UINT64 *LowMemory OPTIONAL,
132 OUT UINT64 *MaxAddress OPTIONAL
133 )
134 {
135 EFI_STATUS Status;
136 FIRMWARE_CONFIG_ITEM FwCfgItem;
137 UINTN FwCfgSize;
138 EFI_E820_ENTRY64 E820Entry;
139 UINTN Processed;
140
141 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);
142 if (EFI_ERROR (Status)) {
143 return Status;
144 }
145
146 if (FwCfgSize % sizeof E820Entry != 0) {
147 return EFI_PROTOCOL_ERROR;
148 }
149
150 if (LowMemory != NULL) {
151 *LowMemory = 0;
152 }
153
154 if (MaxAddress != NULL) {
155 *MaxAddress = BASE_4GB;
156 }
157
158 QemuFwCfgSelectItem (FwCfgItem);
159 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {
160 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);
161 DEBUG ((
162 DEBUG_VERBOSE,
163 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",
164 __FUNCTION__,
165 E820Entry.BaseAddr,
166 E820Entry.Length,
167 E820Entry.Type
168 ));
169 if (E820Entry.Type == EfiAcpiAddressRangeMemory) {
170 if (AddHighHob && (E820Entry.BaseAddr >= BASE_4GB)) {
171 UINT64 Base;
172 UINT64 End;
173
174 //
175 // Round up the start address, and round down the end address.
176 //
177 Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);
178 End = (E820Entry.BaseAddr + E820Entry.Length) &
179 ~(UINT64)EFI_PAGE_MASK;
180 if (Base < End) {
181 PlatformAddMemoryRangeHob (Base, End);
182 DEBUG ((
183 DEBUG_VERBOSE,
184 "%a: PlatformAddMemoryRangeHob [0x%Lx, 0x%Lx)\n",
185 __FUNCTION__,
186 Base,
187 End
188 ));
189 }
190 }
191
192 if (MaxAddress || LowMemory) {
193 UINT64 Candidate;
194
195 Candidate = E820Entry.BaseAddr + E820Entry.Length;
196 if (MaxAddress && (Candidate > *MaxAddress)) {
197 *MaxAddress = Candidate;
198 DEBUG ((
199 DEBUG_VERBOSE,
200 "%a: MaxAddress=0x%Lx\n",
201 __FUNCTION__,
202 *MaxAddress
203 ));
204 }
205
206 if (LowMemory && (Candidate > *LowMemory) && (Candidate < BASE_4GB)) {
207 *LowMemory = Candidate;
208 DEBUG ((
209 DEBUG_VERBOSE,
210 "%a: LowMemory=0x%Lx\n",
211 __FUNCTION__,
212 *LowMemory
213 ));
214 }
215 }
216 }
217 }
218
219 return EFI_SUCCESS;
220 }
221
222 /**
223 Returns PVH memmap
224
225 @param Entries Pointer to PVH memmap
226 @param Count Number of entries
227
228 @return EFI_STATUS
229 **/
230 EFI_STATUS
231 GetPvhMemmapEntries (
232 struct hvm_memmap_table_entry **Entries,
233 UINT32 *Count
234 )
235 {
236 UINT32 *PVHResetVectorData;
237 struct hvm_start_info *pvh_start_info;
238
239 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);
240 if (PVHResetVectorData == 0) {
241 return EFI_NOT_FOUND;
242 }
243
244 pvh_start_info = (struct hvm_start_info *)(UINTN)PVHResetVectorData[0];
245
246 *Entries = (struct hvm_memmap_table_entry *)(UINTN)pvh_start_info->memmap_paddr;
247 *Count = pvh_start_info->memmap_entries;
248
249 return EFI_SUCCESS;
250 }
251
252 STATIC
253 UINT64
254 GetHighestSystemMemoryAddressFromPvhMemmap (
255 BOOLEAN Below4gb
256 )
257 {
258 struct hvm_memmap_table_entry *Memmap;
259 UINT32 MemmapEntriesCount;
260 struct hvm_memmap_table_entry *Entry;
261 EFI_STATUS Status;
262 UINT32 Loop;
263 UINT64 HighestAddress;
264 UINT64 EntryEnd;
265
266 HighestAddress = 0;
267
268 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);
269 ASSERT_EFI_ERROR (Status);
270
271 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {
272 Entry = Memmap + Loop;
273 EntryEnd = Entry->addr + Entry->size;
274
275 if ((Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) &&
276 (EntryEnd > HighestAddress))
277 {
278 if (Below4gb && (EntryEnd <= BASE_4GB)) {
279 HighestAddress = EntryEnd;
280 } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {
281 HighestAddress = EntryEnd;
282 }
283 }
284 }
285
286 return HighestAddress;
287 }
288
289 UINT32
290 EFIAPI
291 PlatformGetSystemMemorySizeBelow4gb (
292 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
293 )
294 {
295 EFI_STATUS Status;
296 UINT64 LowerMemorySize = 0;
297 UINT8 Cmos0x34;
298 UINT8 Cmos0x35;
299
300 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
301 // Get the information from PVH memmap
302 return (UINT32)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE);
303 }
304
305 Status = PlatformScanOrAdd64BitE820Ram (FALSE, &LowerMemorySize, NULL);
306 if ((Status == EFI_SUCCESS) && (LowerMemorySize > 0)) {
307 return (UINT32)LowerMemorySize;
308 }
309
310 //
311 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
312 // * CMOS(0x35) is the high byte
313 // * CMOS(0x34) is the low byte
314 // * The size is specified in 64kb chunks
315 // * Since this is memory above 16MB, the 16MB must be added
316 // into the calculation to get the total memory size.
317 //
318
319 Cmos0x34 = (UINT8)PlatformCmosRead8 (0x34);
320 Cmos0x35 = (UINT8)PlatformCmosRead8 (0x35);
321
322 return (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
323 }
324
325 STATIC
326 UINT64
327 PlatformGetSystemMemorySizeAbove4gb (
328 )
329 {
330 UINT32 Size;
331 UINTN CmosIndex;
332
333 //
334 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
335 // * CMOS(0x5d) is the most significant size byte
336 // * CMOS(0x5c) is the middle size byte
337 // * CMOS(0x5b) is the least significant size byte
338 // * The size is specified in 64kb chunks
339 //
340
341 Size = 0;
342 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {
343 Size = (UINT32)(Size << 8) + (UINT32)PlatformCmosRead8 (CmosIndex);
344 }
345
346 return LShiftU64 (Size, 16);
347 }
348
349 /**
350 Return the highest address that DXE could possibly use, plus one.
351 **/
352 STATIC
353 UINT64
354 PlatformGetFirstNonAddress (
355 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
356 )
357 {
358 UINT64 FirstNonAddress;
359 UINT32 FwCfgPciMmio64Mb;
360 EFI_STATUS Status;
361 FIRMWARE_CONFIG_ITEM FwCfgItem;
362 UINTN FwCfgSize;
363 UINT64 HotPlugMemoryEnd;
364
365 //
366 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
367 //
368 FirstNonAddress = 0;
369
370 //
371 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
372 // address from it. This can express an address >= 4GB+1TB.
373 //
374 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
375 // can only express a size smaller than 1TB), and add it to 4GB.
376 //
377 Status = PlatformScanOrAdd64BitE820Ram (FALSE, NULL, &FirstNonAddress);
378 if (EFI_ERROR (Status)) {
379 FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();
380 }
381
382 //
383 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
384 // resources to 32-bit anyway. See DegradeResource() in
385 // "PciResourceSupport.c".
386 //
387 #ifdef MDE_CPU_IA32
388 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
389 return FirstNonAddress;
390 }
391
392 #endif
393
394 //
395 // See if the user specified the number of megabytes for the 64-bit PCI host
396 // aperture. Accept an aperture size up to 16TB.
397 //
398 // As signaled by the "X-" prefix, this knob is experimental, and might go
399 // away at any time.
400 //
401 Status = QemuFwCfgParseUint32 (
402 "opt/ovmf/X-PciMmio64Mb",
403 FALSE,
404 &FwCfgPciMmio64Mb
405 );
406 switch (Status) {
407 case EFI_UNSUPPORTED:
408 case EFI_NOT_FOUND:
409 break;
410 case EFI_SUCCESS:
411 if (FwCfgPciMmio64Mb <= 0x1000000) {
412 PlatformInfoHob->PcdPciMmio64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);
413 break;
414 }
415
416 //
417 // fall through
418 //
419 default:
420 DEBUG ((
421 DEBUG_WARN,
422 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
423 __FUNCTION__
424 ));
425 break;
426 }
427
428 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
429 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
430 DEBUG ((
431 DEBUG_INFO,
432 "%a: disabling 64-bit PCI host aperture\n",
433 __FUNCTION__
434 ));
435 }
436
437 //
438 // There's nothing more to do; the amount of memory above 4GB fully
439 // determines the highest address plus one. The memory hotplug area (see
440 // below) plays no role for the firmware in this case.
441 //
442 return FirstNonAddress;
443 }
444
445 //
446 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
447 // absolute, exclusive end address for the memory hotplug area. This area
448 // starts right at the end of the memory above 4GB. The 64-bit PCI host
449 // aperture must be placed above it.
450 //
451 Status = QemuFwCfgFindFile (
452 "etc/reserved-memory-end",
453 &FwCfgItem,
454 &FwCfgSize
455 );
456 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {
457 QemuFwCfgSelectItem (FwCfgItem);
458 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);
459 DEBUG ((
460 DEBUG_VERBOSE,
461 "%a: HotPlugMemoryEnd=0x%Lx\n",
462 __FUNCTION__,
463 HotPlugMemoryEnd
464 ));
465
466 ASSERT (HotPlugMemoryEnd >= FirstNonAddress);
467 FirstNonAddress = HotPlugMemoryEnd;
468 }
469
470 //
471 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
472 // that the host can map it with 1GB hugepages. Follow suit.
473 //
474 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);
475 PlatformInfoHob->PcdPciMmio64Size = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Size, (UINT64)SIZE_1GB);
476
477 //
478 // The 64-bit PCI host aperture should also be "naturally" aligned. The
479 // alignment is determined by rounding the size of the aperture down to the
480 // next smaller or equal power of two. That is, align the aperture by the
481 // largest BAR size that can fit into it.
482 //
483 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Base, GetPowerOfTwo64 (PlatformInfoHob->PcdPciMmio64Size));
484
485 //
486 // The useful address space ends with the 64-bit PCI host aperture.
487 //
488 FirstNonAddress = PlatformInfoHob->PcdPciMmio64Base + PlatformInfoHob->PcdPciMmio64Size;
489 return FirstNonAddress;
490 }
491
492 /**
493 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
494 **/
495 VOID
496 EFIAPI
497 PlatformAddressWidthInitialization (
498 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
499 )
500 {
501 UINT64 FirstNonAddress;
502 UINT8 PhysMemAddressWidth;
503
504 //
505 // As guest-physical memory size grows, the permanent PEI RAM requirements
506 // are dominated by the identity-mapping page tables built by the DXE IPL.
507 // The DXL IPL keys off of the physical address bits advertized in the CPU
508 // HOB. To conserve memory, we calculate the minimum address width here.
509 //
510 FirstNonAddress = PlatformGetFirstNonAddress (PlatformInfoHob);
511 PhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);
512
513 //
514 // If FirstNonAddress is not an integral power of two, then we need an
515 // additional bit.
516 //
517 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {
518 ++PhysMemAddressWidth;
519 }
520
521 //
522 // The minimum address width is 36 (covers up to and excluding 64 GB, which
523 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
524 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
525 // can simply assert that here, since 48 bits are good enough for 256 TB.
526 //
527 if (PhysMemAddressWidth <= 36) {
528 PhysMemAddressWidth = 36;
529 }
530
531 ASSERT (PhysMemAddressWidth <= 48);
532
533 PlatformInfoHob->FirstNonAddress = FirstNonAddress;
534 PlatformInfoHob->PhysMemAddressWidth = PhysMemAddressWidth;
535 }
536
537 STATIC
538 VOID
539 QemuInitializeRamBelow1gb (
540 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
541 )
542 {
543 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
544 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);
545 PlatformAddReservedMemoryBaseSizeHob (
546 SMM_DEFAULT_SMBASE,
547 MCH_DEFAULT_SMBASE_SIZE,
548 TRUE /* Cacheable */
549 );
550 STATIC_ASSERT (
551 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,
552 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
553 );
554 PlatformAddMemoryRangeHob (
555 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,
556 BASE_512KB + BASE_128KB
557 );
558 } else {
559 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
560 }
561 }
562
563 /**
564 Peform Memory Detection for QEMU / KVM
565
566 **/
567 VOID
568 EFIAPI
569 PlatformQemuInitializeRam (
570 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
571 )
572 {
573 UINT64 LowerMemorySize;
574 UINT64 UpperMemorySize;
575 MTRR_SETTINGS MtrrSettings;
576 EFI_STATUS Status;
577
578 DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));
579
580 //
581 // Determine total memory size available
582 //
583 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
584
585 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
586 //
587 // Create the following memory HOB as an exception on the S3 boot path.
588 //
589 // Normally we'd create memory HOBs only on the normal boot path. However,
590 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
591 // well, for "borrowing" a subset of it temporarily, for the AP startup
592 // vector.
593 //
594 // CpuMpPei saves the original contents of the borrowed area in permanent
595 // PEI RAM, in a backup buffer allocated with the normal PEI services.
596 // CpuMpPei restores the original contents ("returns" the borrowed area) at
597 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
598 // transferring control to the OS's wakeup vector in the FACS.
599 //
600 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
601 // restore the original contents. Furthermore, we expect all such PEIMs
602 // (CpuMpPei included) to claim the borrowed areas by producing memory
603 // allocation HOBs, and to honor preexistent memory allocation HOBs when
604 // looking for an area to borrow.
605 //
606 QemuInitializeRamBelow1gb (PlatformInfoHob);
607 } else {
608 //
609 // Create memory HOBs
610 //
611 QemuInitializeRamBelow1gb (PlatformInfoHob);
612
613 if (PlatformInfoHob->SmmSmramRequire) {
614 UINT32 TsegSize;
615
616 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
617 PlatformAddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);
618 PlatformAddReservedMemoryBaseSizeHob (
619 LowerMemorySize - TsegSize,
620 TsegSize,
621 TRUE
622 );
623 } else {
624 PlatformAddMemoryRangeHob (BASE_1MB, LowerMemorySize);
625 }
626
627 //
628 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
629 // entries. Otherwise, create a single memory HOB with the flat >=4GB
630 // memory size read from the CMOS.
631 //
632 Status = PlatformScanOrAdd64BitE820Ram (TRUE, NULL, NULL);
633 if (EFI_ERROR (Status)) {
634 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();
635 if (UpperMemorySize != 0) {
636 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
637 }
638 }
639 }
640
641 //
642 // We'd like to keep the following ranges uncached:
643 // - [640 KB, 1 MB)
644 // - [LowerMemorySize, 4 GB)
645 //
646 // Everything else should be WB. Unfortunately, programming the inverse (ie.
647 // keeping the default UC, and configuring the complement set of the above as
648 // WB) is not reliable in general, because the end of the upper RAM can have
649 // practically any alignment, and we may not have enough variable MTRRs to
650 // cover it exactly.
651 //
652 if (IsMtrrSupported () && (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID)) {
653 MtrrGetAllMtrrs (&MtrrSettings);
654
655 //
656 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
657 //
658 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
659 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
660 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
661
662 //
663 // flip default type to writeback
664 //
665 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
666 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
667 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
668 MtrrSetAllMtrrs (&MtrrSettings);
669
670 //
671 // Set memory range from 640KB to 1MB to uncacheable
672 //
673 Status = MtrrSetMemoryAttribute (
674 BASE_512KB + BASE_128KB,
675 BASE_1MB - (BASE_512KB + BASE_128KB),
676 CacheUncacheable
677 );
678 ASSERT_EFI_ERROR (Status);
679
680 //
681 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
682 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
683 //
684 Status = MtrrSetMemoryAttribute (
685 PlatformInfoHob->Uc32Base,
686 SIZE_4GB - PlatformInfoHob->Uc32Base,
687 CacheUncacheable
688 );
689 ASSERT_EFI_ERROR (Status);
690 }
691 }
692
693 VOID
694 EFIAPI
695 PlatformQemuInitializeRamForS3 (
696 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
697 )
698 {
699 if (PlatformInfoHob->S3Supported && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {
700 //
701 // This is the memory range that will be used for PEI on S3 resume
702 //
703 BuildMemoryAllocationHob (
704 PlatformInfoHob->S3AcpiReservedMemoryBase,
705 PlatformInfoHob->S3AcpiReservedMemorySize,
706 EfiACPIMemoryNVS
707 );
708
709 //
710 // Cover the initial RAM area used as stack and temporary PEI heap.
711 //
712 // This is reserved as ACPI NVS so it can be used on S3 resume.
713 //
714 BuildMemoryAllocationHob (
715 PcdGet32 (PcdOvmfSecPeiTempRamBase),
716 PcdGet32 (PcdOvmfSecPeiTempRamSize),
717 EfiACPIMemoryNVS
718 );
719
720 //
721 // SEC stores its table of GUIDed section handlers here.
722 //
723 BuildMemoryAllocationHob (
724 PcdGet64 (PcdGuidedExtractHandlerTableAddress),
725 PcdGet32 (PcdGuidedExtractHandlerTableSize),
726 EfiACPIMemoryNVS
727 );
728
729 #ifdef MDE_CPU_X64
730 //
731 // Reserve the initial page tables built by the reset vector code.
732 //
733 // Since this memory range will be used by the Reset Vector on S3
734 // resume, it must be reserved as ACPI NVS.
735 //
736 BuildMemoryAllocationHob (
737 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),
738 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),
739 EfiACPIMemoryNVS
740 );
741
742 if (PlatformInfoHob->SevEsIsEnabled) {
743 //
744 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
745 // includes the extra page table used to break down the 2MB page
746 // mapping into 4KB page entries where the GHCB resides and the
747 // GHCB area itself.
748 //
749 // Since this memory range will be used by the Reset Vector on S3
750 // resume, it must be reserved as ACPI NVS.
751 //
752 BuildMemoryAllocationHob (
753 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),
754 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),
755 EfiACPIMemoryNVS
756 );
757 BuildMemoryAllocationHob (
758 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),
759 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),
760 EfiACPIMemoryNVS
761 );
762 BuildMemoryAllocationHob (
763 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),
764 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),
765 EfiACPIMemoryNVS
766 );
767 }
768
769 #endif
770 }
771
772 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
773 if (!PlatformInfoHob->SmmSmramRequire) {
774 //
775 // Reserve the lock box storage area
776 //
777 // Since this memory range will be used on S3 resume, it must be
778 // reserved as ACPI NVS.
779 //
780 // If S3 is unsupported, then various drivers might still write to the
781 // LockBox area. We ought to prevent DXE from serving allocation requests
782 // such that they would overlap the LockBox storage.
783 //
784 ZeroMem (
785 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
786 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)
787 );
788 BuildMemoryAllocationHob (
789 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
790 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),
791 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
792 );
793 }
794
795 if (PlatformInfoHob->SmmSmramRequire) {
796 UINT32 TsegSize;
797
798 //
799 // Make sure the TSEG area that we reported as a reserved memory resource
800 // cannot be used for reserved memory allocations.
801 //
802 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
803 BuildMemoryAllocationHob (
804 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob) - TsegSize,
805 TsegSize,
806 EfiReservedMemoryType
807 );
808 //
809 // Similarly, allocate away the (already reserved) SMRAM at the default
810 // SMBASE, if it exists.
811 //
812 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {
813 BuildMemoryAllocationHob (
814 SMM_DEFAULT_SMBASE,
815 MCH_DEFAULT_SMBASE_SIZE,
816 EfiReservedMemoryType
817 );
818 }
819 }
820
821 #ifdef MDE_CPU_X64
822 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {
823 //
824 // Reserve the work area.
825 //
826 // Since this memory range will be used by the Reset Vector on S3
827 // resume, it must be reserved as ACPI NVS.
828 //
829 // If S3 is unsupported, then various drivers might still write to the
830 // work area. We ought to prevent DXE from serving allocation requests
831 // such that they would overlap the work area.
832 //
833 BuildMemoryAllocationHob (
834 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),
835 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),
836 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
837 );
838 }
839
840 #endif
841 }
842 }