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