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