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