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